From 7af30059362801f428c832e83235bb524ac83f1d Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Mon, 22 Sep 2025 16:56:59 +0100 Subject: [PATCH 01/50] Initial changes for IntrinsicCall and named arguments --- src/psyclone/psyir/nodes/intrinsic_call.py | 825 ++++++++++++------ .../gocean_opencl_trans_test.py | 28 +- .../lfric/test_lfric_adjoint_harness.py | 6 +- src/psyclone/tests/psyad/main_test.py | 4 +- src/psyclone/tests/psyir/backend/c_test.py | 13 +- .../tests/psyir/nodes/intrinsic_call_test.py | 210 ++++- .../array_reduction_base_trans_test.py | 60 +- 7 files changed, 814 insertions(+), 332 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 2fc9e4649d..5664c4773c 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -66,8 +66,10 @@ # Named tuple for describing the properties of the required arguments to # a particular intrinsic. If there's no limit on the number of arguments -# then `max_count` will be None. -ArgDesc = namedtuple('ArgDesc', 'min_count max_count types') +# then `max_count` will be None. If max_count is not None, then arg_names +# will contain a list of the argument names of the required arguments, in +# the order defined by the standard. +ArgDesc = namedtuple('ArgDesc', 'min_count max_count types arg_names') class IntrinsicCall(Call): @@ -112,7 +114,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, None, Reference), + required_args=ArgDesc(1, None, Reference, ((None,),)), optional_args={ "mold": Reference, "source": Reference, @@ -127,7 +129,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, None, Reference), + required_args=ArgDesc(1, None, Reference, ((None,),)), optional_args={"stat": Reference}, return_type=None, reference_accesses=None, @@ -137,7 +139,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, None, Reference), + required_args=ArgDesc(1, None, Reference, ((None,),)), optional_args={}, return_type=None, reference_accesses=None, @@ -149,7 +151,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -159,7 +161,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -169,7 +171,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -179,7 +181,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -189,7 +191,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("string",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -199,7 +201,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("string",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -209,7 +211,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("z",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -219,7 +221,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -229,7 +231,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("mask",),)), optional_args={"dim": DataNode}, return_type=None, reference_accesses=None, @@ -239,7 +241,8 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + # Argname of allocated depends on the input. + required_args=ArgDesc(1, 1, DataNode, (("",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -249,7 +252,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -259,7 +262,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("mask",),)), optional_args={"dim": DataNode}, return_type=None, reference_accesses=None, @@ -269,7 +272,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -279,7 +282,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -289,7 +292,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("pointer",),)), optional_args={"target": DataNode}, return_type=None, reference_accesses=None, @@ -299,7 +302,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc(1, 2, DataNode, (("x",), ("y", "x"))), optional_args={}, # N. B. If this has 2 arguments then the return value # is the of the second argument, however the standard defines @@ -312,7 +315,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("y", "x"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -322,7 +325,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -332,7 +335,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -342,7 +345,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -352,7 +355,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 4, 4, DataNode, ( + ("atom", "old", "compare", "new"), + ) + ), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -362,7 +369,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -372,7 +379,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc( + 3, 3, DataNode, ( + ("atom", "value", "old"), + ) + ), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -382,7 +393,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc( + 3, 3, DataNode, ( + ("atom", "value", "old"), + ) + ), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -392,7 +407,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc( + 3, 3, DataNode, ( + ("atom", "value", "old"), + ) + ), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -402,7 +421,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc( + 3, 3, DataNode, ( + ("atom", "value", "old"), + ) + ), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -412,7 +435,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -422,7 +445,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("value", "atom"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -432,7 +455,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -442,7 +465,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -452,7 +475,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -464,7 +487,12 @@ class Intrinsic(IAttr, Enum): # structure of the IntrinsicCall. is_elemental=None, is_inquiry=False, - required_args=ArgDesc(2, 3, DataNode), + required_args=ArgDesc( + 2, 3, DataNode, ( + ("n", "x"), + ("n1", "n2", "x"), + ) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -474,7 +502,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -484,7 +512,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -496,7 +524,12 @@ class Intrinsic(IAttr, Enum): # structure of the IntrinsicCall. is_elemental=None, is_inquiry=False, - required_args=ArgDesc(2, 3, DataNode), + required_args=ArgDesc( + 2, 3, DataNode, ( + ("n", "x"), + ("n1", "n2", "x"), + ) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -506,7 +539,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -516,7 +549,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -526,7 +559,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -536,7 +569,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -546,7 +579,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -556,7 +589,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "pos"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -566,7 +599,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -576,7 +609,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -586,7 +619,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={"Y": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -596,7 +629,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, ( + ("a", "source_image"), + ) + ), optional_args={"stat": DataNode, "errmsg": DataNode}, return_type=None, reference_accesses=None, @@ -606,7 +643,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -618,7 +655,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -630,7 +667,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, ( + ("a", "operation"), + ) + ), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -642,7 +683,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -654,7 +695,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, None), + required_args=ArgDesc(0, 0, None, ()), optional_args={}, return_type=None, reference_accesses=None, @@ -664,7 +705,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("z",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -674,7 +715,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -684,7 +725,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -694,7 +735,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("coarray",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -704,7 +745,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("mask",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -714,7 +755,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("time",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -724,7 +765,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("array", "shift"),)), optional_args={"dim": DataNode}, return_type=None, reference_accesses=None, @@ -734,7 +775,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={ "date": DataNode, "time": DataNode, @@ -749,7 +790,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -759,7 +800,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -769,7 +810,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("x", "y"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -779,7 +820,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, (("vector_a", "vector_b"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -789,7 +832,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("x", "y"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -799,7 +842,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc(3, 3, DataNode, (("i", "j", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -809,7 +852,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc(3, 3, DataNode, (("i", "j", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -819,7 +862,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("array", "shift"),)), optional_args={"boundary": DataNode, "dim": DataNode}, return_type=None, reference_accesses=None, @@ -829,7 +872,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -839,7 +882,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -849,7 +892,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -859,7 +902,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -869,7 +912,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("event", "count"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -879,7 +922,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("command",),)), optional_args={ "wait": DataNode, "exitstat": DataNode, @@ -894,7 +937,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -904,7 +947,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -914,7 +957,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("a", "mold"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -924,7 +967,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={"team": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -934,7 +977,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 3, DataNode), + required_args=ArgDesc( + 2, 3, DataNode, ( + ("array", "value", "dim"), + ("array", "value") + ) + ), optional_args={"mask": DataNode, "kind": DataNode, "back": DataNode}, @@ -946,7 +994,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -956,7 +1004,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -966,7 +1014,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -976,7 +1024,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -986,7 +1034,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={ "command": DataNode, "length": DataNode, @@ -1001,7 +1049,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("number",),)), optional_args={ "value": DataNode, "length": DataNode, @@ -1016,7 +1064,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("name",),)), optional_args={ "value": DataNode, "length": DataNode, @@ -1032,7 +1080,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={"level": DataNode}, return_type=None, reference_accesses=None, @@ -1042,7 +1090,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, (Reference, Literal)), + required_args=ArgDesc(1, 1, (Reference, Literal), (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1052,7 +1100,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("x", "y"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1062,7 +1110,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("c",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1072,8 +1120,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), - optional_args={"dim": DataNode, "mask": DataNode}, + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), + optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, ) @@ -1082,7 +1135,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1092,8 +1145,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), - optional_args={"dim": DataNode, "mask": DataNode}, + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), + optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, ) @@ -1102,7 +1160,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "pos"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1112,7 +1170,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc(3, 3, DataNode, (("i", "pos", "len"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1122,7 +1180,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "pos"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1132,7 +1190,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("c",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1142,7 +1200,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1152,7 +1210,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(2, 3, DataNode), + # Argument names depend on input, as TEAM vs TEAM_NUMBER + # are not distinguishable without context. + required_args=ArgDesc(2, 3, DataNode, (("",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1162,7 +1222,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("image",),)), optional_args={"team": DataNode}, return_type=None, reference_accesses=None, @@ -1172,7 +1232,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("string", "substring"),)), optional_args={"back": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1182,7 +1242,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1192,7 +1252,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1202,7 +1262,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, @@ -1212,7 +1277,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("array",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1222,7 +1287,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1232,7 +1297,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1242,7 +1307,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1252,7 +1317,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("i", "shift"))), optional_args={"size": DataNode}, return_type=None, reference_accesses=None, @@ -1262,7 +1327,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1272,7 +1337,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("array",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1282,7 +1347,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("coarray",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1292,7 +1357,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1302,7 +1367,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("string",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1312,7 +1377,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("string",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1322,7 +1387,10 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, + (("string_a", "string_b"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1332,7 +1400,10 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, + (("string_a", "string_b"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1342,7 +1413,10 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, + (("string_a", "string_b"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1352,7 +1426,10 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, + (("string_a", "string_b"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1362,7 +1439,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1372,7 +1449,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1382,7 +1459,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1392,7 +1469,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("l",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1402,7 +1479,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1412,7 +1489,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1422,7 +1499,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc( + 2, 2, DataNode, (("matrix_a", "matrix_b"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1432,7 +1511,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, None, DataNode), + # No upper limit on argument type so we don't store an + # argument list of names. + required_args=ArgDesc(2, None, DataNode, ((None,),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1442,7 +1523,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1452,9 +1533,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), optional_args={ - "dim": DataNode, "mask": DataNode, "kind": DataNode, "back": DataNode, @@ -1467,8 +1552,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), - optional_args={"dim": DataNode, "mask": DataNode}, + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), + optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, ) @@ -1477,7 +1567,8 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc(3, 3, DataNode, + (("tsource", "fsource", "mask"),)), optional_args={}, return_type=None, reference_accesses=None @@ -1487,7 +1578,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc(3, 3, DataNode, (("i", "j", "mask"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1497,7 +1588,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, None, DataNode), + # No upper limit on argument type so we don't store an + # argument list of names. + required_args=ArgDesc(2, None, DataNode, ((None,),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1507,7 +1600,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1517,9 +1610,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), optional_args={ - "dim": DataNode, "mask": DataNode, "kind": DataNode, "back": DataNode, @@ -1532,8 +1629,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), - optional_args={"dim": DataNode, "mask": DataNode}, + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), + optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, ) @@ -1542,7 +1644,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("a", "p"),)), optional_args={}, return_type=None, reference_accesses=None @@ -1552,7 +1654,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("a", "p"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1562,7 +1664,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("from", "to"),)), optional_args={"stat": DataNode, "errmsg": DataNode}, return_type=None, reference_accesses=None, @@ -1572,7 +1674,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(5, 5, DataNode), + required_args=ArgDesc( + 5, 5, DataNode, ( + ("from", "frompos", "len", "to", "topos"), + ) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1582,7 +1688,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("x", "s"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1592,7 +1698,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("c"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1602,7 +1708,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1612,7 +1718,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 1, 2, DataNode, ( + ("x",), + ("x", "dim") + ) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1622,7 +1733,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1632,7 +1743,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={"mold": DataNode}, return_type=None, reference_accesses=None, @@ -1642,7 +1753,8 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 1, DataNode), + # Argnames depends on the input. + required_args=ArgDesc(0, 1, DataNode, (("",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1652,7 +1764,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("x", "mold",),)), optional_args={"round": DataNode}, return_type=None, reference_accesses=None, @@ -1662,7 +1774,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("array", "mask"),)), optional_args={"vector": DataNode}, return_type=None, reference_accesses=None, @@ -1672,7 +1784,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode), + required_args=ArgDesc( + 1, 2, DataNode, ( + ("mask",), + ("mask", "dim") + ) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1682,7 +1799,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1692,7 +1809,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1702,7 +1819,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1712,7 +1829,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1722,8 +1839,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), - optional_args={"dim": DataNode, "mask": DataNode}, + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), + optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, ) @@ -1732,7 +1854,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1742,7 +1864,8 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, + (("repeatable", "image_distinct"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1752,7 +1875,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("harvest",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1762,7 +1885,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, Reference), + required_args=ArgDesc(0, 0, Reference, ()), optional_args={"size": DataNode, "put": DataNode, "Get": DataNode}, return_type=None, reference_accesses=None, @@ -1772,7 +1895,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1782,7 +1905,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("a",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1792,7 +1915,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1802,7 +1925,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 3, DataNode), + required_args=ArgDesc( + 2, 3, DataNode, ( + ("array", "operation"), + ("array", "operation", "dim") + ) + ), optional_args={"mask": DataNode, "identity": DataNode, "ordered": DataNode}, @@ -1814,7 +1942,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("string", "ncopies"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1824,7 +1952,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("source", "shape"),)), optional_args={"pad": DataNode, "order": DataNode}, return_type=None, reference_accesses=None, @@ -1834,7 +1962,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1844,7 +1972,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("a", "b"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1854,7 +1982,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("x", "i"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1864,7 +1992,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("string", "set"),)), optional_args={"back": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1874,7 +2002,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("name",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1884,7 +2012,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("r",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1894,7 +2022,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, Reference), + required_args=ArgDesc(0, 0, Reference, ()), optional_args={"P": DataNode, "R": DataNode, "radix": DataNode}, return_type=None, reference_accesses=None, @@ -1904,7 +2032,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("x", "i"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1914,7 +2042,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, Reference), + required_args=ArgDesc(1, 1, Reference, (("source",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1924,7 +2052,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1934,7 +2062,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1944,7 +2072,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference), + required_args=ArgDesc(2, 2, Reference, (("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1954,7 +2082,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("a", "b"),)), optional_args={}, return_type=None, reference_accesses=None @@ -1964,7 +2092,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1974,7 +2102,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1984,7 +2112,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("array",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1994,7 +2122,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2004,7 +2132,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc(3, 3, DataNode, ( + ("source", "dim", "ncopies"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -2014,7 +2144,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2024,7 +2154,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={"team": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2034,7 +2164,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -2044,8 +2174,13 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), - optional_args={"dim": DataNode, "mask": DataNode}, + required_args=ArgDesc( + 1, 2, DataNode, ( + ("array",), + ("array", "dim") + ) + ), + optional_args={"mask": DataNode}, return_type=None, reference_accesses=None, ) @@ -2054,7 +2189,7 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={"count": DataNode, "count_rate": DataNode, "count_max": DataNode}, @@ -2066,7 +2201,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2076,7 +2211,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2086,7 +2221,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), + required_args=ArgDesc(0, 0, DataNode, ()), optional_args={"team": DataNode}, return_type=None, reference_accesses=None, @@ -2096,10 +2231,14 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode), - optional_args={"coarray": DataNode, - "team": DataNode, - "dim": DataNode}, + required_args=ArgDesc( + 0, 2, DataNode, ( + (), + ("coarray",), + ("coarray", "dim") + ) + ), + optional_args={"team": DataNode}, return_type=None, reference_accesses=None, ) @@ -2108,7 +2247,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, (Reference, Literal)), + required_args=ArgDesc(1, 1, (Reference, Literal), (("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2118,7 +2257,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2128,7 +2267,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("source", "mold"),)), optional_args={"size": DataNode}, return_type=None, reference_accesses=None, @@ -2138,7 +2277,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("matrix",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2148,7 +2287,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("string",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2158,7 +2297,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("array",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2168,7 +2307,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode), + required_args=ArgDesc(1, 1, DataNode, (("coarray",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2178,7 +2317,9 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode), + required_args=ArgDesc( + 3, 3, DataNode, (("vector", "mask", "field"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -2188,7 +2329,7 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode), + required_args=ArgDesc(2, 2, DataNode, (("string", "set"),)), optional_args={"back": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2251,6 +2392,207 @@ def is_available_on_device(self, device_string: str = "") -> bool: f"Unsupported device_string value '{device_string}', the supported" " values are '' (default), 'nvfortran-all', 'nvfortran-uniform'") + def canonicalise(self): + '''Canonicalise an IntrinsicCall in the PSyIR. Upon successful + canonicalisation, all arguments will become named arguments and + arguments may be reordered to match what is defined in the standard + for the argument ordering. + + A small number of intrinsics (e.g. ALLOCATE) never have ambiguity + and no argument limits, in which case no canonicalisation is done. + + :raises ValueError: If the number of arguments or argument names + are not valid for this IntrinsicCall. + :raises NotImplementedError: If there is argument ambiguity and + canonicalisation is not possible. + ''' + # Get the optional argument names + optional_names = list(self.intrinsic.optional_args.keys()) + + # If PSyclone can't handle the required args arg_names due + # to them being non-finite or context sensitive, then skip + # checking argument names (This is if [0][0] is None or ''). + if (not (len(self.intrinsic.required_args.arg_names) == 1 and + not self.intrinsic.required_args.arg_names[0][0])): + # Get all valid argument names. + all_valid_names = [ + name for tupl in self.intrinsic.required_args.arg_names for + name in tupl + ] + all_valid_names.extend(optional_names) + # Check we have valid argument names. + # Raise ValueError if not. + for name in self.argument_names: + if not name: + continue + if name not in all_valid_names: + raise ValueError( + f"Found invalid argument name '{name}' when " + f"canonicalising the '{self.intrinsic.name}' " + f"IntrinsicCall. Allowed argument names are " + f"'{sorted(set(all_valid_names))}'." + ) + + # Check we have a valid number of arguments + if len(self.arguments) < self.intrinsic.required_args.min_count: + raise ValueError( + f"Found too few arguments when canonicalising the " + f"'{self.intrinsic.name}' IntrinsicCall. Requires at " + f"least {self.intrinsic.required_args.min_count} " + f"arguments but found {len(self.arguments)}." + ) + + # If there is no maximum number of required arguments then we + # can skip the rest of canonicalisation, as this Intrinsic can never + # have ambiguity. + if self.intrinsic.required_args.max_count is None: + return + + if (len(self.arguments) > (self.intrinsic.required_args.max_count + + len(optional_names))): + raise ValueError( + f"Found too many arguments when canonicalising the " + f"'{self.intrinsic.name}' IntrinsicCall. Requires at " + f"most {self.intrinsic.required_args.max_count} " + f"arguments but found {len(self.arguments)}." + ) + + # Find which argument list we are canonicalising + if len(self.intrinsic.required_args.arg_names) > 1: + available_args = list(range(len( + self.intrinsic.required_args.arg_names + ))) + # Discount any of the required argument lists that don't contain + # a named non-optional argument + for name in self.argument_names: + if not name: + continue + if name in optional_names: + continue + for i, arglist in enumerate( + self.intrinsic.required_args.arg_names): + if i not in available_args: + continue + if name not in arglist: + available_args.remove(i) + + # Remove any of the argument lists that we have too many or + # too few total arguments to handle. + for choice in available_args[:]: + min_args = len(self.intrinsic.required_args.arg_names[choice]) + max_args = min_args + len(optional_names) + if (len(self.arguments) < min_args or + len(self.arguments) > max_args): + available_args.remove(choice) + + # Remove any of the argument lists that we have too many or + # too few required arguments to handle. + # At this point the total arguments must be valid for all + # remaining choices, and all named arguments must also be + # present. + for choice in available_args[:]: + required_args = len( + self.intrinsic.required_args.arg_names[choice] + ) + # Check if the number of unnamed arguments is greater + # than the number of required arguments. If so then + # this choice is still acceptable. + if (len([x for x in self.argument_names if x is None]) >= + required_args): + continue + # Otherwise we need to check if all the + # required arguments are present as named arguments. + remaining_required = self.intrinsic.required_args.\ + arg_names[choice][len([x for x in self.argument_names + if x is None]):] + for name in remaining_required: + if name not in self.argument_names: + available_args.remove(choice) + break + + # If we still have more than one available argument list here + # then we can't canonicalise + if len(available_args) > 1 or len(available_args) == 0: + raise NotImplementedError( + f"Cannot canonicalise '{self.intrinsic.name}' " + f"IntrinsicCall as PSyclone can't determine which " + f"argument set it should use. This can be resolved by " + f"using named arguments in the Fortran source." + ) + arg_list = self.intrinsic.required_args.arg_names[ + available_args[0] + ] + elif len(self.intrinsic.required_args.arg_names) == 1: + arg_list = self.intrinsic.required_args.arg_names[0] + else: + arg_list = () + + # Handle cases where None or "" is in the arg_list, as this implies + # context sensitive argument naming which PSyclone cannot handle. + if arg_list and not arg_list[0]: + # If we find any named non-optional name arguments for these + # intrinsics then we can't canonicalise this IntrinsicCall. + # N.B. With currently supported intrinsic there are no + # optional argument on these context-sensitive intrinsics + # that have a finite argument count, but we keep the check + # in case we need the support in future, and it still handles + # what we currently need to check (i.e. if we have a named + # argument here we can't canonicalise it safely). + for name in self.argument_names: + if name not in optional_names: + raise NotImplementedError( + f"Cannot canonicalise '{self.intrinsic.name}' " + f"as non-optional argument name '{name}' found " + f"but the Intrinsic has context-sensitive argument " + f"names which is unsupported by PSyclone." + ) + + # The following rules are defined by the Fortran standard. + # 1. Unnamed arguments must be in the order defined in the standard, + # i.e. you cannot have LBOUND(1, 8, array=i). + # 2. If all arguments are named, the order is entirely flexible, so + # LBOUND(kind=8, dim=1, array=i) is allowed. + # 3. All unnamed arguments will occur before any named arguments. + + # Name any unnamed arguments. + for i, arg in enumerate(self.argument_names): + # If we find a named arg then we can exit this section. + if arg: + break + if i < len(arg_list): + # We found a required argument without a name. + self._argument_names[i] = (self._argument_names[i][0], + arg_list[i]) + continue + # Otherwise we found an optional argument, which will always + # be in order if unnamed. + self._argument_names[i] = (self._argument_names[i][0], + optional_names[i - len(arg_list)]) + + # We have all arguments named now, we want to reorder them. + new_arg_names = [] + new_args = [] + + for required in arg_list: + index = self.argument_names.index(required) + new_arg_names.append(self._argument_names[index]) + new_args.append(self.arguments[index]) + + for option in optional_names: + if option not in self.argument_names: + continue + index = self.argument_names.index(option) + new_arg_names.append(self._argument_names[index]) + new_args.append(self.arguments[index]) + + # Replace the argument list with the canonicalised version. + if len(new_args) > 0: + for child in self.children[1:]: + child.detach() + for child in new_args: + self.addchild(child) + self._argument_names = new_arg_names + @classmethod def create(cls, intrinsic, arguments=()): '''Create an instance of this class given the type of intrinsic and a @@ -2284,7 +2626,11 @@ def create(cls, intrinsic, arguments=()): # Validate the supplied arguments. last_named_arg = None - pos_arg_count = 0 + # Get all valid required argument names. + valid_req_names = [ + name for tupl in intrinsic.required_args.arg_names for + name in tupl + ] for arg in arguments: if isinstance(arg, tuple): if not isinstance(arg[0], str): @@ -2294,19 +2640,6 @@ def create(cls, intrinsic, arguments=()): f"a {type(arg[0]).__name__} instead of a str.") name = arg[0].lower() last_named_arg = name - # TODO #2302: For now we disable the positional arguments - # checks because this does not consider that positional - # arguments can be also found by name, and we don't have - # sufficient information to validate them. - # if not optional_arg_names: - # raise ValueError( - # f"The '{intrinsic.name}' intrinsic does not support " - # f"any optional arguments but got '{name}'.") - # if name not in optional_arg_names: - # raise ValueError( - # f"The '{intrinsic.name}' intrinsic supports the " - # f"optional arguments {optional_arg_names} but got " - # f"'{name}'") if name in intrinsic.optional_args: if not isinstance(arg[1], intrinsic.optional_args[name]): raise TypeError( @@ -2314,9 +2647,13 @@ def create(cls, intrinsic, arguments=()): f"'{intrinsic.name}' must be of type " f"'{intrinsic.optional_args[name].__name__}' but " f"got '{type(arg[1]).__name__}'") - else: - # If it not in the optional_args list it must be positional - pos_arg_count += 1 + elif name in valid_req_names: + if not isinstance(arg[1], intrinsic.required_args.types): + raise TypeError( + f"The argument '{name}' to intrinsic " + f"'{intrinsic.name}' must be of type " + f"'{intrinsic.required_args.types.__name__}' but " + f"got '{type(arg[1]).__name__}'") else: if last_named_arg: raise ValueError( @@ -2328,20 +2665,6 @@ def create(cls, intrinsic, arguments=()): f"positional arguments be of type " f"'{intrinsic.required_args.types}' " f"but got a '{type(arg).__name__}'") - pos_arg_count += 1 - - if ((intrinsic.required_args.max_count is not None and - pos_arg_count > intrinsic.required_args.max_count) - or pos_arg_count < intrinsic.required_args.min_count): - msg = f"The '{intrinsic.name}' intrinsic requires " - if (intrinsic.required_args.max_count is not None and - intrinsic.required_args.max_count > 0): - msg += (f"between {intrinsic.required_args.min_count} and " - f"{intrinsic.required_args.max_count} ") - else: - msg += f"at least {intrinsic.required_args.min_count} " - msg += f"arguments but got {len(arguments)}." - raise ValueError(msg) # Create an intrinsic call and add the arguments # afterwards. We can't call the parent create method as it @@ -2350,6 +2673,16 @@ def create(cls, intrinsic, arguments=()): # the intrinsic enum. call._add_args(call, arguments) + # Error check and canoniclise the call + try: + call.canonicalise() + except (ValueError, NotImplementedError) as err: + for child in call.children: + child.detach() + # Rereaise the error with the same type and message as the + # original error. + raise type(err)(err.args[0]) from err + return call def reference_accesses(self) -> VariablesAccessMap: diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py index bd154e4e2c..4cb49ffdcc 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py @@ -260,9 +260,9 @@ def test_invoke_opencl_initialisation(kernel_outputdir, fortran_writer): call initialise_device_buffer(u_fld) ! do a set_args now so subsequent writes place the data appropriately - cu_fld_cl_mem = transfer(cu_fld%device_ptr, cu_fld_cl_mem) - p_fld_cl_mem = transfer(p_fld%device_ptr, p_fld_cl_mem) - u_fld_cl_mem = transfer(u_fld%device_ptr, u_fld_cl_mem) + cu_fld_cl_mem = transfer(source=cu_fld%device_ptr, mold=cu_fld_cl_mem) + p_fld_cl_mem = transfer(source=p_fld%device_ptr, mold=p_fld_cl_mem) + u_fld_cl_mem = transfer(source=u_fld%device_ptr, mold=u_fld_cl_mem) call compute_cu_code_set_args(kernel_compute_cu_code, cu_fld_cl_mem, \ p_fld_cl_mem, u_fld_cl_mem, xstart - 1, xstop - 1, ystart - 1, ystop - 1) @@ -384,17 +384,18 @@ def test_invoke_opencl_initialisation_grid(): call initialise_grid_device_buffers(in_fld) ! do a set_args now so subsequent writes place the data appropriately - out_fld_cl_mem = transfer(out_fld%device_ptr, out_fld_cl_mem) - in_out_fld_cl_mem = transfer(in_out_fld%device_ptr, in_out_fld_cl_mem) - in_fld_cl_mem = transfer(in_fld%device_ptr, in_fld_cl_mem) - dx_cl_mem = transfer(dx%device_ptr, dx_cl_mem) - gphiu_cl_mem = transfer(in_fld%grid%gphiu_device, gphiu_cl_mem) + out_fld_cl_mem = transfer(source=out_fld%device_ptr, mold=out_fld_cl_mem) + in_out_fld_cl_mem = transfer(source=in_out_fld%device_ptr, mold=in_out_fld_cl_mem) + in_fld_cl_mem = transfer(source=in_fld%device_ptr, mold=in_fld_cl_mem) + dx_cl_mem = transfer(source=dx%device_ptr, mold=dx_cl_mem) + gphiu_cl_mem = transfer(source=in_fld%grid%gphiu_device, mold=gphiu_cl_mem) call compute_kernel_code_set_args(kernel_compute_kernel_code, \ out_fld_cl_mem, in_out_fld_cl_mem, in_fld_cl_mem, dx_cl_mem, \ in_fld%grid%dx, gphiu_cl_mem, xstart - 1, xstop - 1, ystart - 1, \ ystop - 1) ! write data to the device''' + print(generated_code) assert expected in generated_code # The write_to_device() can appear in any order in the following 5 lines @@ -707,7 +708,7 @@ def test_invoke_opencl_kernel_call(kernel_outputdir, monkeypatch, debug_mode): # Check that the globalsize first dimension is a multiple of # the localsize first dimension expected += ''' - if (MOD(p_fld%grid%nx, 64) /= 0) then + if (MOD(a=p_fld%grid%nx, p=64) /= 0) then call check_status('Global size is not a multiple of local size \ (mandatory in OpenCL < 2.0).', -1) end if''' @@ -721,9 +722,9 @@ def test_invoke_opencl_kernel_call(kernel_outputdir, monkeypatch, debug_mode): # Cast dl_esm_inf pointers to cl_mem handlers expected += ''' - cu_fld_cl_mem = TRANSFER(cu_fld%device_ptr, cu_fld_cl_mem) - p_fld_cl_mem = TRANSFER(p_fld%device_ptr, p_fld_cl_mem) - u_fld_cl_mem = TRANSFER(u_fld%device_ptr, u_fld_cl_mem)''' + cu_fld_cl_mem = TRANSFER(source=cu_fld%device_ptr, mold=cu_fld_cl_mem) + p_fld_cl_mem = TRANSFER(source=p_fld%device_ptr, mold=p_fld_cl_mem) + u_fld_cl_mem = TRANSFER(source=u_fld%device_ptr, mold=u_fld_cl_mem)''' # Call the set_args subroutine with the boundaries corrected for the # OpenCL 0-indexing @@ -747,7 +748,6 @@ def test_invoke_opencl_kernel_call(kernel_outputdir, monkeypatch, debug_mode): call check_status('compute_cu_code clEnqueueNDRangeKernel', ierr) ierr = clFinish(cmd_queues(1)) call check_status('Errors during compute_cu_code', ierr)''' - assert expected in generated_code assert GOceanOpenCLBuild(kernel_outputdir).code_compiles(psy) @@ -964,7 +964,7 @@ def test_multiple_command_queues(dist_mem): kernelbarrier = ''' ierr = clFinish(cmd_queues(2)) - p_fld_cl_mem = TRANSFER(p_fld%device_ptr, p_fld_cl_mem)''' + p_fld_cl_mem = TRANSFER(source=p_fld%device_ptr, mold=p_fld_cl_mem)''' haloexbarrier = ''' ierr = clFinish(cmd_queues(2)) diff --git a/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py b/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py index 54341a9492..2543f07adb 100644 --- a/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py +++ b/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py @@ -458,7 +458,7 @@ def test_lfric_create_real_comparison(fortran_writer): " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" - " relative_diff = ABS(var1 - var2) / MachineTol\n" + " relative_diff = ABS(a=var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" " ! - Unsupported statement: Write_Stmt\n" @@ -500,7 +500,7 @@ def test_lfric_log_write(fortran_writer): " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" - " relative_diff = ABS(var1 - var2) / MachineTol\n" + " relative_diff = ABS(a=var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" " ! - Unsupported statement: Write_Stmt\n" @@ -594,7 +594,7 @@ def test_generate_lfric_adjoint_harness(fortran_reader, fortran_writer): assert ("call field_input%initialise(vector_space=vector_space_w3_ptr," " name='field_input')" in gen) # So too must the scalar argument. - assert (" call random_number(ascalar)\n" + assert (" call random_number(harvest=ascalar)\n" " ascalar_input = ascalar\n" in gen) # The field must be given random values and those copied into the copy. diff --git a/src/psyclone/tests/psyad/main_test.py b/src/psyclone/tests/psyad/main_test.py index 68fc4686de..32bcaea41e 100644 --- a/src/psyclone/tests/psyad/main_test.py +++ b/src/psyclone/tests/psyad/main_test.py @@ -151,7 +151,7 @@ ! test the inner-product values for equality, allowing for the precision \ of the active variables machinetol = spacing(max(abs(inner1), abs(inner2))) - relative_diff = abs(inner1 - inner2) / machinetol + relative_diff = abs(a=inner1 - inner2) / machinetol if (relative_diff < overall_tolerance) then ! psyclone codeblock (unsupported code) reason: ! - unsupported statement: write_stmt @@ -534,6 +534,8 @@ def test_main_otest_option(tmpdir, capsys, extra_args): assert output == "" with open(harness_out, 'r', encoding='utf-8') as my_file: data = my_file.read() + print(EXPECTED_HARNESS_CODE) + print(data.lower()) assert EXPECTED_HARNESS_CODE in data.lower() diff --git a/src/psyclone/tests/psyir/backend/c_test.py b/src/psyclone/tests/psyir/backend/c_test.py index ee7edbf03c..f3cf24bb8b 100644 --- a/src/psyclone/tests/psyir/backend/c_test.py +++ b/src/psyclone/tests/psyir/backend/c_test.py @@ -386,10 +386,8 @@ def test_cw_intrinsiccall(): # Check that operator-style formatting with a number of children different # than 2 produces an error with pytest.raises(VisitorError) as err: - icall.children[0].replace_with( - Reference( - IntrinsicSymbol(IntrinsicCall.Intrinsic.MOD.name, - IntrinsicCall.Intrinsic.MOD))) + icall = IntrinsicCall(IntrinsicCall.Intrinsic.MOD) + icall.addchild(ref1.copy()) _ = cwriter(icall) assert ("The C Writer binary_operator formatter for IntrinsicCall only " "supports intrinsics with 2 children, but found '%' with '1' " @@ -408,10 +406,9 @@ def test_cw_intrinsiccall(): # Check that casts with more than one children produce an error with pytest.raises(VisitorError) as err: - icall.children[0].replace_with( - Reference( - IntrinsicSymbol(IntrinsicCall.Intrinsic.REAL.name, - IntrinsicCall.Intrinsic.REAL))) + icall = IntrinsicCall(IntrinsicCall.Intrinsic.REAL) + icall.addchild(ref1.copy()) + icall.addchild(ref2.copy()) _ = cwriter(icall) assert ("The C Writer IntrinsicCall cast-style formatter only supports " "intrinsics with 1 child, but found 'float' with '2' children." diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index 4f6a66c2c4..7c03f75967 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -279,28 +279,32 @@ def test_intrinsiccall_minmaxsum_create(intrinsic_call): # array and optional dim intrinsic = IntrinsicCall.create( intrinsic_call, [Reference(array), ("dim", Reference(dim))]) - assert intrinsic.argument_names == [None, "dim"] + assert intrinsic.argument_names == ["array", "dim"] # array and optional mask intrinsic = IntrinsicCall.create( intrinsic_call, [Reference(array), ("mask", Reference(mask))]) - assert intrinsic.argument_names == [None, "mask"] + assert intrinsic.argument_names == ["array", "mask"] # array and optional dim then optional mask intrinsic = IntrinsicCall.create( intrinsic_call, [Reference(array), ("dim", Reference(dim)), ("mask", Reference(mask))]) - assert intrinsic.argument_names == [None, "dim", "mask"] + assert intrinsic.argument_names == ["array", "dim", "mask"] # array and optional mask then optional dim intrinsic = IntrinsicCall.create( intrinsic_call, [Reference(array), ("mask", Reference(mask)), ("dim", Reference(dim))]) - assert intrinsic.argument_names == [None, "mask", "dim"] + assert intrinsic.argument_names == ["array", "dim", "mask"] + assert intrinsic.children[2].symbol.name == "dim" + assert intrinsic.children[3].symbol.name == "mask" # array and optional literal mask and optional literal dim intrinsic = IntrinsicCall.create( intrinsic_call, [ Reference(array), ("mask", Literal("1", INTEGER_TYPE)), ("dim", Literal("false", BOOLEAN_TYPE))]) - assert intrinsic.argument_names == [None, "mask", "dim"] + assert intrinsic.argument_names == ["array", "dim", "mask"] + assert intrinsic.children[2].value == "false" + assert intrinsic.children[3].value == "1" @pytest.mark.parametrize("intrinsic_call", [ @@ -349,14 +353,16 @@ def test_intrinsiccall_create_errors(): # An allocate must have one or more References as argument. with pytest.raises(ValueError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.ALLOCATE, []) - assert ("The 'ALLOCATE' intrinsic requires at least 1 arguments but " - "got 0" in str(err.value)) + assert ("Found too few arguments when canonicalising the 'ALLOCATE' " + "IntrinsicCall. Requires at least 1 arguments but found 0." + in str(err.value)) # The random intrinsic only accepts one argument. with pytest.raises(ValueError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, [aref, aref.copy()]) - assert ("The 'RANDOM_NUMBER' intrinsic requires between 1 and 1 arguments " - "but got 2" in str(err.value)) + assert ("Found too many arguments when canonicalising the 'RANDOM_NUMBER'" + " IntrinsicCall. Requires at most 1 arguments but found 2." + in str(err.value)) # Wrong type for a positional argument. with pytest.raises(TypeError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.ALLOCATE, @@ -364,6 +370,12 @@ def test_intrinsiccall_create_errors(): assert ("The 'ALLOCATE' intrinsic requires that positional arguments be " "of type " in str(err.value)) assert "but got a 'DataSymbol'" in str(err.value) + # Wrong type for a named position argument. + with pytest.raises(TypeError) as err: + IntrinsicCall.create(IntrinsicCall.Intrinsic.TAN, + [("x", sym)]) + assert ("The argument 'x' to intrinsic 'TAN' must be of type 'DataNode' " + "but got 'DataSymbol'" in str(err.value)) # Positional argument after named argument. with pytest.raises(ValueError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.DEALLOCATE, @@ -371,23 +383,13 @@ def test_intrinsiccall_create_errors(): assert ("Found a positional argument *after* a named argument ('stat'). " "This is invalid." in str(err.value)) - # TODO #2303: We can not enable the validation of positional parameters - # unless we store their name, otherwise when we parse a positional argument - # by name, which is valid fortran, it will fail. - # (e.g. RANDOM_NUMBER(harvest=4) - - # with pytest.raises(ValueError) as err: - # IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, - # [aref, ("willow", sym)]) - # assert ("The 'RANDOM_NUMBER' intrinsic does not support any optional " - # "arguments but got 'willow'" in str(err.value)) - # An allocate only supports the 'stat' and 'mold' arguments. - # with pytest.raises(ValueError) as err: - # IntrinsicCall.create(IntrinsicCall.Intrinsic.ALLOCATE, - # [aref, ("yacht", Reference(sym))]) - # assert ("The 'ALLOCATE' intrinsic supports the optional arguments " - # "['errmsg', 'mold', 'source', 'stat'] but got 'yacht'" - # in str(err.value)) + # Test invalid optional argument provision + with pytest.raises(ValueError) as err: + IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, + [aref.detach(), ("willow", Reference(sym))]) + assert ("Found invalid argument name 'willow' when canonicalising the " + "'RANDOM_NUMBER' IntrinsicCall. Allowed argument names are " + "'['harvest']'." in str(err.value)) # Wrong type for the name of an optional argument. with pytest.raises(TypeError) as err: @@ -417,14 +419,14 @@ def test_create_positional_arguments_with_names(): assert isinstance(intr, IntrinsicCall) assert intr.arguments[0] == aref assert intr.arguments[1] == bref - assert intr.argument_names == [None, None] + assert intr.argument_names == ["vector_a", "vector_b"] intr = IntrinsicCall.create(IntrinsicCall.Intrinsic.DOT_PRODUCT, [aref.copy(), ("vector_b", bref.copy())]) assert isinstance(intr, IntrinsicCall) assert intr.arguments[0] == aref assert intr.arguments[1] == bref - assert intr.argument_names == [None, "vector_b"] + assert intr.argument_names == ["vector_a", "vector_b"] intr = IntrinsicCall.create(IntrinsicCall.Intrinsic.DOT_PRODUCT, [("vector_a", aref.copy()), @@ -438,9 +440,9 @@ def test_create_positional_arguments_with_names(): [("vector_b", bref.copy()), ("vector_a", aref.copy())]) assert isinstance(intr, IntrinsicCall) - assert intr.arguments[0] == bref - assert intr.arguments[1] == aref - assert intr.argument_names == ["vector_b", "vector_a"] + assert intr.arguments[0] == aref + assert intr.arguments[1] == bref + assert intr.argument_names == ["vector_a", "vector_b"] @pytest.mark.parametrize("operator", ["lbound", "ubound", "size"]) @@ -579,3 +581,149 @@ def test_verify_intrinsic(fortran_reader, fortran_writer): "== 0) then" in result) assert ("if (verify(clname(ind1:ind2), '0123456789', kind=kind(1), " "back=.true.) == 0) then" in result) + + +def test_intrinsic_canonicalisation(fortran_reader): + ''' + Test the canonicalisation function of the IntrinsicCall class. + ''' + + # Test canonicalisation fails if we have an incorrect name argument. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + # Set up the argument_names array + intrinsic.argument_names + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "wrong") + with pytest.raises(ValueError) as err: + intrinsic.canonicalise() + assert ("Found invalid argument name 'wrong' when canonicalising the " + "'SUM' IntrinsicCall. Allowed argument names are " + "'['array', 'dim', 'mask']'." in str(err.value)) + + # Test canonicalisation fails if we don't have enough arguments. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + with pytest.raises(ValueError) as err: + intrinsic.canonicalise() + assert ("Found too few arguments when canonicalising the 'SUM' " + "IntrinsicCall. Requires at least 1 arguments but found 0." + in str(err.value)) + + # Test canonicalisation fails if we have too many arguments + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + with pytest.raises(ValueError) as err: + intrinsic.canonicalise() + assert ("Found too many arguments when canonicalising the 'SUM' " + "IntrinsicCall. Requires at most 2 arguments but found 4." + in str(err.value)) + + # Test canonicalisation works if we have 1 argument for SUM. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.canonicalise() + assert intrinsic.argument_names[0] == "array" + + # Test canonicalisation doesn't work when we have 2 arguments for SUM + # with no naming, as it can't determine between the SUM variants. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + with pytest.raises(NotImplementedError) as err: + intrinsic.canonicalise() + assert ("Cannot canonicalise 'SUM' IntrinsicCall as PSyclone can't " + "determine which argument set it should use. This can be " + "resolved by using named arguments in the Fortran source." + in str(err.value)) + + # Test canonicalisation does work when we give a name to the 2nd argument. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + # Set up the argument_names array and set the second ones name to be mask + intrinsic.argument_names + intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "mask") + intrinsic.canonicalise() + assert intrinsic.argument_names == ["array", "mask"] + + # Test we only ge the correct canonicalisation when we have a named + # argument only in one of the lists. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + # Set up the argument_names array and set the second ones name to be dim + intrinsic.argument_names + intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "dim") + intrinsic.canonicalise() + + # The only case I can see that can hit line 2473 + # (i not in available args: continue) is an invalid BESSEL_JN Intrinsic + # I think at the moment, though if we can support context specific + # names we do need that. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.BESSEL_JN) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + # Set up the argument_names array and set the argument names + intrinsic.argument_names + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "n1") + intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "n2") + with pytest.raises(NotImplementedError) as err: + intrinsic.canonicalise() + assert ("Cannot canonicalise 'BESSEL_JN' IntrinsicCall as PSyclone can't " + "determine which argument set it should use. This can be resolved " + "by using named arguments in the Fortran source" + in str(err.value)) + + # Check we can canonicalise an intrinsic with only one argument set. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SIN) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.canonicalise() + assert intrinsic.argument_names == ["x"] + + # Check that canonicalisation succeeds for an intrinsic with no required + # arguments + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SYSTEM_CLOCK) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) + intrinsic.argument_names + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], + "count_rate") + intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], + "count_max") + intrinsic._argument_names[2] = (intrinsic._argument_names[2][0], "count") + intrinsic.canonicalise() + assert intrinsic.argument_names == ["count", "count_rate", "count_max"] + assert intrinsic.children[1].symbol.name == "c" + assert intrinsic.children[2].symbol.name == "a" + assert intrinsic.children[3].symbol.name == "b" + + # Test canonicliation for intrinsic when PSyclone doesn't support named + # non-optional arguments. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATE) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) + intrinsic.argument_names + intrinsic._argument_names[2] = (intrinsic._argument_names[2][0], "mold") + intrinsic.canonicalise() + assert intrinsic.argument_names == [None, None, "mold"] + + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.argument_names + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "array") + with pytest.raises(NotImplementedError) as err: + intrinsic.canonicalise() + assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " + "'array' found but the Intrinsic has context-sensitive argument " + "names which is unsupported by PSyclone." in str(err.value)) + + # Check that we canoncalise when we have unnamed optional arguments. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) + intrinsic.canonicalise() + assert intrinsic.argument_names == ["array", "dim", "mask"] diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py index b1001bc2c4..00223775c1 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py @@ -262,10 +262,10 @@ def test_validate_increment_with_unsupported_type(fortran_reader): [("10", "20", "1", "10", "1", "20"), ("n", "m", "1", "n", "1", "m"), ("0:n", "2:m", "0", "n", "2", "m"), - (":", ":", "LBOUND(array, dim=1)", - "UBOUND(array, dim=1)", - "LBOUND(array, dim=2)", - "UBOUND(array, dim=2)")]) + (":", ":", "LBOUND(array=array, dim=1)", + "UBOUND(array=array, dim=1)", + "LBOUND(array=array, dim=2)", + "UBOUND(array=array, dim=2)")]) def test_apply(idim1, idim2, rdim11, rdim12, rdim21, rdim22, fortran_reader, fortran_writer, tmpdir): '''Test that a maxval intrinsic as the only term on the rhs of an @@ -283,7 +283,7 @@ def test_apply(idim1, idim2, rdim11, rdim12, rdim21, rdim22, f" result = maxval(array)\n" f"end subroutine\n") expected = ( - f" result = -HUGE(result)\n" + f" result = -HUGE(x=result)\n" f" do idx = {rdim21}, {rdim22}, 1\n" f" do idx_1 = {rdim11}, {rdim12}, 1\n" f" result = MAX(result, array(idx_1,idx))\n" @@ -316,7 +316,7 @@ def test_apply_multi(fortran_reader, fortran_writer, tmpdir): " result = value1 + maxval(array) * value2\n" "end subroutine\n") expected = ( - " result = -HUGE(result)\n" + " result = -HUGE(x=result)\n" " do idx = 1, m, 1\n" " do idx_1 = 1, n, 1\n" " result = MAX(result, array(idx_1,idx))\n" @@ -370,7 +370,7 @@ def test_mask(fortran_reader, fortran_writer, tmpdir): " result = maxval(array, mask=MOD(array, 2.0)==1)\n" "end program\n") expected = ( - " result = -HUGE(result)\n" + " result = -HUGE(x=result)\n" " do idx = 1, 10, 1\n" " do idx_1 = 1, 10, 1\n" " if (MOD(array(idx_1,idx), 2.0) == 1) then\n" @@ -406,7 +406,7 @@ def test_mask_array_indexed(fortran_reader, fortran_writer, tmpdir): " result = maxval(a, mask=a(1)>a)\n" "end program\n") expected = ( - " result = -HUGE(result)\n" + " result = -HUGE(x=result)\n" " do idx = 1, 4, 1\n" " if (a(1) > a(idx)) then\n" " result = MAX(result, a(idx))\n" @@ -439,10 +439,10 @@ def test_allocate(fortran_reader, fortran_writer, tmpdir): "end program\n") expected = ( " ALLOCATE(a(1:4,1:4,1:4))\n" - " result = -HUGE(result)\n" - " do idx = LBOUND(a, dim=3), UBOUND(a, dim=3), 1\n" - " do idx_1 = LBOUND(a, dim=2), UBOUND(a, dim=2), 1\n" - " do idx_2 = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" + " result = -HUGE(x=result)\n" + " do idx = LBOUND(array=a, dim=3), UBOUND(array=a, dim=3), 1\n" + " do idx_1 = LBOUND(array=a, dim=2), UBOUND(array=a, dim=2), 1\n" + " do idx_2 = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" " result = MAX(result, a(idx_2,idx_1,idx))\n" " enddo\n" " enddo\n" @@ -473,7 +473,7 @@ def test_references(fortran_reader, fortran_writer, tmpdir): "zmax(1) = MAXVAL(ABS(sshn + ssh_ref * tmask), mask=tmask==1.0)\n" "end subroutine\n") expected = ( - " zmax(1) = -HUGE(zmax(1))\n" + " zmax(1) = -HUGE(x=zmax(1))\n" " do idx = 1, 10, 1\n" " do idx_1 = 1, 10, 1\n" " if (tmask(idx_1,idx) == 1.0) then\n" @@ -502,9 +502,10 @@ def test_nemo_example(fortran_reader, fortran_writer, tmpdir): "zmax(1) = MAXVAL(ABS(sshn(:,:) + ssh_ref * tmask(:,:,1)))\n" "end subroutine\n") expected = ( - " zmax(1) = -HUGE(zmax(1))\n" - " do idx = LBOUND(sshn, dim=2), UBOUND(sshn, dim=2), 1\n" - " do idx_1 = LBOUND(sshn, dim=1), UBOUND(sshn, dim=1), 1\n" + " zmax(1) = -HUGE(x=zmax(1))\n" + " do idx = LBOUND(array=sshn, dim=2), UBOUND(array=sshn, dim=2), 1\n" + " do idx_1 = LBOUND(array=sshn, dim=1), " + "UBOUND(array=sshn, dim=1), 1\n" " zmax(1) = MAX(zmax(1), ABS(sshn(idx_1,idx) + ssh_ref * " "tmask(idx_1,idx,1)))\n" " enddo\n" @@ -531,8 +532,8 @@ def test_constant_dims(fortran_reader, fortran_writer, tmpdir): "x = maxval(a(:,1)+b(10,:), mask=c(:)==1.0)\n" "end subroutine\n") expected = ( - " x = -HUGE(x)\n" - " do idx = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" + " x = -HUGE(x=x)\n" + " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" " if (c(idx) == 1.0) then\n" " x = MAX(x, a(idx,1) + b(10,idx))\n" " end if\n" @@ -564,8 +565,8 @@ def test_expression_1d(fortran_reader, fortran_writer, tmpdir): " real, dimension(10) :: b\n" " real :: x\n" " integer :: idx\n\n" - " x = -HUGE(x)\n" - " do idx = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" + " x = -HUGE(x=x)\n" + " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" " x = MAX(x, a(idx) + b(idx))\n" " enddo\n\n" "end subroutine test\n") @@ -597,10 +598,11 @@ def test_expression_3d(fortran_reader, fortran_writer, tmpdir): " integer :: idx\n" " integer :: idx_1\n" " integer :: idx_2\n\n" - " x = -HUGE(x)\n" - " do idx = LBOUND(a, dim=3), UBOUND(a, dim=3), 1\n" - " do idx_1 = LBOUND(a, dim=2), UBOUND(a, dim=2), 1\n" - " do idx_2 = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" + " x = -HUGE(x=x)\n" + " do idx = LBOUND(array=a, dim=3), UBOUND(array=a, dim=3), 1\n" + " do idx_1 = LBOUND(array=a, dim=2), UBOUND(array=a, dim=2), 1\n" + " do idx_2 = LBOUND(array=a, dim=1), " + "UBOUND(array=a, dim=1), 1\n" " x = MAX(x, -a(idx_2,idx_1,idx) + 10.0)\n" " enddo\n" " enddo\n" @@ -628,8 +630,8 @@ def test_multi_intrinsics(fortran_reader, fortran_writer, tmpdir): "x = maxval(a(:)) + maxval(b(:))\n" "end subroutine\n") expected = ( - " x = -HUGE(x)\n" - " do idx = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" + " x = -HUGE(x=x)\n" + " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" " x = MAX(x, a(idx))\n" " enddo\n" " x = x + MAXVAL(b(:))\n") @@ -655,7 +657,7 @@ def test_increment(fortran_reader, fortran_writer, tmpdir): "x = x + maxval(a)\n" "end subroutine\n") expected = ( - " tmp_var = -HUGE(tmp_var)\n" + " tmp_var = -HUGE(x=tmp_var)\n" " do idx = 1, 10, 1\n" " tmp_var = MAX(tmp_var, a(idx))\n" " enddo\n" @@ -683,7 +685,7 @@ def test_increment_with_accessor(fortran_reader, fortran_writer, tmpdir): "end subroutine\n") expected_decl = "real :: tmp_var" expected = ( - " tmp_var = -HUGE(tmp_var)\n" + " tmp_var = -HUGE(x=tmp_var)\n" " do idx = 1, 10, 1\n" " tmp_var = MAX(tmp_var, a(idx))\n" " enddo\n" @@ -710,7 +712,7 @@ def test_reduce_to_struct_and_array_accessors(fortran_reader, fortran_writer): "mystruct%x(3) = maxval(a)\n" "end subroutine\n") expected = ( - " mystruct%x(3) = -HUGE(mystruct%x(3))\n" + " mystruct%x(3) = -HUGE(x=mystruct%x(3))\n" " do idx = 1, 10, 1\n" " mystruct%x(3) = MAX(mystruct%x(3), a(idx))\n" " enddo\n") From 5dd0720182e0b08ae9e9f78024bfdb1e8b1b3688 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Mon, 22 Sep 2025 16:58:26 +0100 Subject: [PATCH 02/50] linting --- .../gocean/transformations/gocean_opencl_trans_test.py | 6 ++++-- src/psyclone/tests/psyir/backend/c_test.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py index 4cb49ffdcc..ccf2af705f 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py @@ -385,10 +385,12 @@ def test_invoke_opencl_initialisation_grid(): ! do a set_args now so subsequent writes place the data appropriately out_fld_cl_mem = transfer(source=out_fld%device_ptr, mold=out_fld_cl_mem) - in_out_fld_cl_mem = transfer(source=in_out_fld%device_ptr, mold=in_out_fld_cl_mem) + in_out_fld_cl_mem = transfer(source=in_out_fld%device_ptr, \ +mold=in_out_fld_cl_mem) in_fld_cl_mem = transfer(source=in_fld%device_ptr, mold=in_fld_cl_mem) dx_cl_mem = transfer(source=dx%device_ptr, mold=dx_cl_mem) - gphiu_cl_mem = transfer(source=in_fld%grid%gphiu_device, mold=gphiu_cl_mem) + gphiu_cl_mem = transfer(source=in_fld%grid%gphiu_device, \ +mold=gphiu_cl_mem) call compute_kernel_code_set_args(kernel_compute_kernel_code, \ out_fld_cl_mem, in_out_fld_cl_mem, in_fld_cl_mem, dx_cl_mem, \ in_fld%grid%dx, gphiu_cl_mem, xstart - 1, xstop - 1, ystart - 1, \ diff --git a/src/psyclone/tests/psyir/backend/c_test.py b/src/psyclone/tests/psyir/backend/c_test.py index f3cf24bb8b..c8b2741ecc 100644 --- a/src/psyclone/tests/psyir/backend/c_test.py +++ b/src/psyclone/tests/psyir/backend/c_test.py @@ -48,7 +48,7 @@ IntrinsicCall) from psyclone.psyir.symbols import ( ArgumentInterface, ArrayType, BOOLEAN_TYPE, CHARACTER_TYPE, DataSymbol, - INTEGER_TYPE, REAL_TYPE, IntrinsicSymbol) + INTEGER_TYPE, REAL_TYPE) def test_cw_gen_declaration(): From 0a371cae4469252a4799a176df65d7b902d8e744 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 23 Sep 2025 11:43:53 +0100 Subject: [PATCH 03/50] Added canonicalisation of intrinsics into the frontend --- src/psyclone/psyir/frontend/fparser2.py | 18 +-- src/psyclone/psyir/nodes/intrinsic_call.py | 2 + .../tests/psyir/nodes/intrinsic_call_test.py | 6 + .../arrayassignment2loops_trans_test.py | 110 +++++++++--------- .../intrinsics/abs2code_trans_test.py | 6 +- .../intrinsics/dotproduct2code_trans_test.py | 8 +- .../intrinsics/matmul2code_trans_test.py | 4 +- .../intrinsics/maxval2loop_trans_test.py | 4 +- .../intrinsics/minval2loop_trans_test.py | 4 +- .../intrinsics/sign2code_trans_test.py | 8 +- 10 files changed, 94 insertions(+), 76 deletions(-) diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index 06752d170e..e13ce745dc 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -3087,6 +3087,7 @@ def _allocate_handler(self, node, parent): (e.g. allocate(character(len=10) :: my_var)). ''' + # Canonicalise doesn't do anything for ALLOCATE so we don't need to. call = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATE, parent=parent) type_spec = node.children[0] @@ -3242,6 +3243,7 @@ def _deallocate_handler(self, node, parent): :rtype: :py:class:`psyclone.psyir.nodes.IntrinsicCall` ''' + # Canonicalise doesn't do anything for DEALLOCATE so we don't need to. call = IntrinsicCall( IntrinsicCall.Intrinsic.DEALLOCATE, parent=parent) dealloc_list = node.children[0].children @@ -4969,18 +4971,20 @@ def _intrinsic_handler(self, node, parent): if not intrinsic.optional_args: # Intrinsics with no optional arguments call = IntrinsicCall(intrinsic, parent=parent) - return self._process_args(node, call) + call = self._process_args(node, call) + call.canonicalise() + return call if intrinsic.name.lower() in ["minval", "maxval", "sum"]: # Intrinsics with optional arguments require a # canonicalise function call = IntrinsicCall(intrinsic, parent=parent) - return self._process_args( - node, call, canonicalise=_canonicalise_minmaxsum) - # TODO #2302: We do not canonicalise the order of the - # arguments of the remaining intrinsics, but this means - # PSyIR won't be able to guarantee what each child is. + call = self._process_args(node, call) + call.canonicalise() + return call call = IntrinsicCall(intrinsic, parent=parent) - return self._process_args(node, call) + call = self._process_args(node, call) + call.canonicalise() + return call except KeyError as err: raise NotImplementedError( f"Intrinsic '{node.items[0].string}' is not supported" diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 5664c4773c..4719b22118 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2539,6 +2539,8 @@ def canonicalise(self): # what we currently need to check (i.e. if we have a named # argument here we can't canonicalise it safely). for name in self.argument_names: + if not name: + continue if name not in optional_names: raise NotImplementedError( f"Cannot canonicalise '{self.intrinsic.name}' " diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index 7c03f75967..e259aa2dfb 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -727,3 +727,9 @@ def test_intrinsic_canonicalisation(fortran_reader): intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) intrinsic.canonicalise() assert intrinsic.argument_names == ["array", "dim", "mask"] + + # Check that we don't fail when the required argument name is None + # and no argument name is supported. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.canonicalise() diff --git a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py index 8dca4de65e..3b0d5bc906 100644 --- a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py @@ -65,68 +65,70 @@ def test_str(): # Scalar RHS [("integer, dimension(:) :: x, y, z, t\n" "x(:) = 0.0", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = 0.0\n"), # Array LHS and RHS ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y(:)\n", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = y(idx)\n"), # Multi-dimensional array LHS and RHS ("integer, dimension(:,:,:) :: x, y, z, t\n" "x(:,:,:) = y(:,:,:)\n", - " do idx = LBOUND(x, dim=3), UBOUND(x, dim=3), 1\n" - " do idx_1 = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" - " do idx_2 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=3), UBOUND(array=x, dim=3), 1\n" + " do idx_1 = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" + " do idx_2 = LBOUND(array=x, dim=1), " + "UBOUND(array=x, dim=1), 1\n" " x(idx_2,idx_1,idx) = y(idx_2,idx_1,idx)\n"), # Multiple arrays on RHS ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y(:) + z(:) * t(:)\n", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = y(idx) + z(idx) * t(idx)\n"), # Argument of elemental functions are expanded ("integer, dimension(:) :: x, y, z, t\n" "x(:) = max(y(:), z(:))\n", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = MAX(y(idx), z(idx))\n"), # Argument of inquiry functions are NOT expanded ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y + size(y)\n", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = y(idx) + SIZE(y)\n"), # Mix different array ranks with fixed indices ("integer, dimension(:) :: x, z, t\n" "integer, dimension(:,:) :: y\n" "x(:) = y(n,:)\n", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = y(n,idx)\n"), ("integer, dimension(:,:) :: x, z, t\n" "integer, dimension(:) :: y\n" "x(n,:) = y(:)\n", - " do idx = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" + " do idx = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" " x(n,idx) = y(idx)\n"), ("integer, dimension(:,:) :: x, z, t\n" "integer, dimension(:,:,:) :: y\n" "x(:,:)=y(:,n,:)\n", - " do idx = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" - " do idx_1 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" + " do idx_1 = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx_1,idx) = y(idx_1,n,idx)\n"), # Same rank but different range locations ("integer, parameter :: jpi=2, jpj=4, jpk=6, jpt=9, ndim=10\n" "real, dimension(jpi,jpj,jpk,jpt,ndim) :: x, y, z, t\n" "x(:,jpj,:,ndim,:) = y(jpi,:,:,:,ndim) + 1.0\n", - " do idx = LBOUND(x, dim=5), UBOUND(x, dim=5), 1\n" - " do idx_1 = LBOUND(x, dim=3), UBOUND(x, dim=3), 1\n" - " do idx_2 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=5), UBOUND(array=x, dim=5), 1\n" + " do idx_1 = LBOUND(array=x, dim=3), UBOUND(array=x, dim=3), 1\n" + " do idx_2 = LBOUND(array=x, dim=1), " + "UBOUND(array=x, dim=1), 1\n" " x(idx_2,jpj,idx_1,ndim,idx) = y(jpi,idx_2,idx_1," "idx,ndim) + 1.0\n" " enddo\n" @@ -151,14 +153,14 @@ def test_str(): # uses L/UBOUND which si correct). ("integer, dimension(2:4) :: x, y, z, t\n" "x(:) = 0", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = 0\n"), # Explicit lower bound value (assumed-shape array) - still just # uses LBOUND. ("integer, dimension(2:) :: x, y, z, t\n" "x(:) = 0", - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = 0\n"), # Combine multiple previous features @@ -171,41 +173,42 @@ def test_str(): # SoA in LHS ("integer :: x, y, z, t\n" "mystruct%soa%array(:,:,:) = 0.0d0", - " do idx = LBOUND(mystruct%soa%array, dim=3), " - "UBOUND(mystruct%soa%array, dim=3), 1\n" - " do idx_1 = LBOUND(mystruct%soa%array, dim=2), " - "UBOUND(mystruct%soa%array, dim=2), 1\n" - " do idx_2 = LBOUND(mystruct%soa%array, dim=1), " - "UBOUND(mystruct%soa%array, dim=1), 1\n" + " do idx = LBOUND(array=mystruct%soa%array, dim=3), " + "UBOUND(array=mystruct%soa%array, dim=3), 1\n" + " do idx_1 = LBOUND(array=mystruct%soa%array, dim=2), " + "UBOUND(array=mystruct%soa%array, dim=2), 1\n" + " do idx_2 = LBOUND(array=mystruct%soa%array, dim=1), " + "UBOUND(array=mystruct%soa%array, dim=1), 1\n" " mystruct%soa%array(idx_2,idx_1,idx) = 0.0d0\n"), # Array in LHS and SoA in RHS ("integer, dimension(:,:,:) :: x, y, z, t\n" "x(:,:,:) = 3 + mystruct%soa%array(:,:,:)", - " do idx = LBOUND(x, dim=3), UBOUND(x, dim=3), 1\n" - " do idx_1 = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" - " do idx_2 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=3), UBOUND(array=x, dim=3), 1\n" + " do idx_1 = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" + " do idx_2 = LBOUND(array=x, dim=1), " + "UBOUND(array=x, dim=1), 1\n" # Ignore offset for this test, it is tested below " x(idx_2,idx_1,idx) = 3 + mystruct%soa%array(idx_2 + "), # SoAoS on LHS ("integer :: x, y, z, t\n" "mystruct%aos(:,:,:)%value = 0.0d0", - " do idx = LBOUND(mystruct%aos, dim=3), " - "UBOUND(mystruct%aos, dim=3), 1\n" - " do idx_1 = LBOUND(mystruct%aos, dim=2), " - "UBOUND(mystruct%aos, dim=2), 1\n" - " do idx_2 = LBOUND(mystruct%aos, dim=1), " - "UBOUND(mystruct%aos, dim=1), 1\n" + " do idx = LBOUND(array=mystruct%aos, dim=3), " + "UBOUND(array=mystruct%aos, dim=3), 1\n" + " do idx_1 = LBOUND(array=mystruct%aos, dim=2), " + "UBOUND(array=mystruct%aos, dim=2), 1\n" + " do idx_2 = LBOUND(array=mystruct%aos, dim=1), " + "UBOUND(array=mystruct%aos, dim=1), 1\n" " mystruct%aos(idx_2,idx_1,idx)%value = 0.0d0\n"), # SoAoS in the LHS and SoA in the RHS ("integer :: x, y, z, t\n" "mystruct%aos(:,4,:)%value = mystruct%soa%array(3,:,:)", - " do idx = LBOUND(mystruct%aos, dim=3), " - "UBOUND(mystruct%aos, dim=3), 1\n" - " do idx_1 = LBOUND(mystruct%aos, dim=1), " - "UBOUND(mystruct%aos, dim=1), 1\n" + " do idx = LBOUND(array=mystruct%aos, dim=3), " + "UBOUND(array=mystruct%aos, dim=3), 1\n" + " do idx_1 = LBOUND(array=mystruct%aos, dim=1), " + "UBOUND(array=mystruct%aos, dim=1), 1\n" # Ignore offset for this test, it is tested below " mystruct%aos(idx_1,4,idx)%value = " "mystruct%soa%array(3,idx_1 + "), @@ -214,12 +217,12 @@ def test_str(): ("integer :: x, y, z, t\n" "mystruct%aoa(4, 3)%array(:,:,:) = " "mystruct%aoa(5, 8)%array(:,:,:)", - " do idx = LBOUND(mystruct%aoa(4,3)%array, dim=3), " - "UBOUND(mystruct%aoa(4,3)%array, dim=3), 1\n" - " do idx_1 = LBOUND(mystruct%aoa(4,3)%array, dim=2), " - "UBOUND(mystruct%aoa(4,3)%array, dim=2), 1\n" - " do idx_2 = LBOUND(mystruct%aoa(4,3)%array, dim=1), " - "UBOUND(mystruct%aoa(4,3)%array, dim=1), 1\n" + " do idx = LBOUND(array=mystruct%aoa(4,3)%array, dim=3), " + "UBOUND(array=mystruct%aoa(4,3)%array, dim=3), 1\n" + " do idx_1 = LBOUND(array=mystruct%aoa(4,3)%array, dim=2), " + "UBOUND(array=mystruct%aoa(4,3)%array, dim=2), 1\n" + " do idx_2 = LBOUND(array=mystruct%aoa(4,3)%array, dim=1), " + "UBOUND(array=mystruct%aoa(4,3)%array, dim=1), 1\n" # Ignore offset for this test, it is tested below " mystruct%aoa(4,3)%array(idx_2,idx_1,idx) = " "mystruct%aoa(5,8)%array(idx_2 +")]) @@ -283,15 +286,17 @@ def test_apply_to_arrays_with_different_bounds(fortran_reader, fortran_writer): # The bounds are not known, so L/UBOUND expressions are used output_test1 = fortran_writer(psyir.walk(Assignment)[0]) - assert ("x1(idx_1,idx) = y1(idx_1 + (LBOUND(y1, dim=1) - LBOUND(x1, " - "dim=1)),idx + (LBOUND(y1, dim=2) - LBOUND(x1, dim=2)))" + assert ("x1(idx_1,idx) = y1(idx_1 + (LBOUND(array=y1, dim=1) " + "- LBOUND(array=x1, dim=1)),idx + " + "(LBOUND(array=y1, dim=2) - LBOUND(array=x1, dim=2)))" in output_test1) # When we know the bounds we can see they are different, we also # need the offsets (and can also use L/UBOUND) output_test2 = fortran_writer(psyir.walk(Assignment)[1]) - assert ("x2(idx_3,idx_2) = y2(idx_3 + (LBOUND(y2, dim=1) - LBOUND(x2, " - "dim=1)),idx_2 + (LBOUND(y2, dim=2) - LBOUND(x2, dim=2)))" + assert ("x2(idx_3,idx_2) = y2(idx_3 + (LBOUND(array=y2, dim=1) " + "- LBOUND(array=x2, dim=1)),idx_2 + " + "(LBOUND(array=y2, dim=2) - LBOUND(array=x2, dim=2)))" in output_test2) # If the bounds are implicit, the offset should also use the implicit @@ -305,9 +310,10 @@ def test_apply_to_arrays_with_different_bounds(fortran_reader, fortran_writer): # SoA and SoAoS bounds are also constructed using L/UBOUNDS expressions output_test4 = fortran_writer(psyir.walk(Assignment)[3]) - assert (" struct%values(idx_6 + (LBOUND(struct%values, dim=1) - " - "LBOUND(x, dim=1))) + struct%array(idx_6 + (" - "LBOUND(struct%array, dim=1) - LBOUND(x, dim=1)))%value" + assert (" struct%values(idx_6 + (LBOUND(array=struct%values, dim=1) - " + "LBOUND(array=x, dim=1))) + struct%array(idx_6 + (" + "LBOUND(array=struct%array, dim=1) - " + "LBOUND(array=x, dim=1)))%value" in output_test4) @@ -384,7 +390,7 @@ def test_apply_assumed_shape(fortran_reader, fortran_writer, tmpdir): trans = ArrayAssignment2LoopsTrans() trans.apply(assign) result = fortran_writer(psyir) - assert '''do idx = istart, UBOUND(var, dim=1), 1 + assert '''do idx = istart, UBOUND(array=var, dim=1), 1 var(idx) = 2 * var2(idx + (istart2 - istart)) enddo''' in result assert Compile(tmpdir).string_compiles(result) @@ -707,10 +713,10 @@ def test_validate_rhs_plain_references(fortran_reader, fortran_writer): # The end result should look like: assert ( - " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx) = scalar\n" " enddo\n" - " do idx_1 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " do idx_1 = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" " x(idx_1) = array(idx_1)\n" " enddo\n\n" " ! ArrayAssignment2LoopsTrans cannot expand expression because it " diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py index f77d8bcbdd..a9b58445ca 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py @@ -102,7 +102,7 @@ def test_correct(func, output, tmpdir): f"subroutine abs_example(arg)\n" f" real, intent(inout) :: arg\n" f" real :: psyir_tmp\n\n" - f" psyir_tmp = ABS({output})\n\n" + f" psyir_tmp = ABS(a={output})\n\n" f"end subroutine abs_example\n") in result trans = Abs2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -149,7 +149,7 @@ def test_correct_expr(tmpdir): "subroutine abs_example(arg)\n" " real, intent(inout) :: arg\n" " real :: psyir_tmp\n\n" - " psyir_tmp = 1.0 + ABS(arg * 3.14) + 2.0\n\n" + " psyir_tmp = 1.0 + ABS(a=arg * 3.14) + 2.0\n\n" "end subroutine abs_example\n") in result trans = Abs2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -196,7 +196,7 @@ def test_correct_2abs(tmpdir): "subroutine abs_example(arg)\n" " real, intent(inout) :: arg\n" " real :: psyir_tmp\n\n" - " psyir_tmp = ABS(arg * 3.14) + ABS(1.0)\n\n" + " psyir_tmp = ABS(a=arg * 3.14) + ABS(a=1.0)\n\n" "end subroutine abs_example\n") in result trans = Abs2CodeTrans() trans.apply(intr_call, root.symbol_table) diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py index 7d6e7d1dc1..6acfe13e61 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py @@ -320,7 +320,7 @@ def test_apply_unknown_dims(tmpdir, fortran_reader, fortran_writer): " integer :: i\n" " real(kind=r_def) :: res_dot_product\n\n" " res_dot_product = 0.0\n" - " do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" + " do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" " res_dot_product = res_dot_product + v1(i) * v2(i)\n" " enddo\n" " result = res_dot_product\n\n") @@ -369,7 +369,7 @@ def test_apply_array_notation( " integer :: i\n" " real :: res_dot_product\n\n" " res_dot_product = 0.0\n" - " do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" + " do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" " res_dot_product = res_dot_product + v1(i) * v2(i)\n" " enddo\n" " result = res_dot_product\n\n") @@ -397,7 +397,7 @@ def test_apply_extra_dims(tmpdir, fortran_reader, fortran_writer, arg1, arg2, f" integer :: i\n" f" real :: res_dot_product\n\n" f" res_dot_product = 0.0\n" - f" do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" + f" do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" f" res_dot_product = res_dot_product + v1{res1} * v2{res2}\n" f" enddo\n" f" result = res_dot_product\n\n") @@ -426,7 +426,7 @@ def test_apply_extra_dims_sizes(tmpdir, fortran_reader, fortran_writer, f" integer :: i\n" f" real :: res_dot_product\n\n" f" res_dot_product = 0.0\n" - f" do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" + f" do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" f" res_dot_product = res_dot_product + v1{res1} * v2{res2}\n" f" enddo\n" f" result = res_dot_product\n\n") diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py index f50fb1fb17..0f7b5f8a09 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py @@ -256,8 +256,8 @@ def test_validate_arg_not_ref(): with pytest.raises(TransformationError) as excinfo: trans.validate(matmul) assert ("Expected result and operands of MATMUL IntrinsicCall to be " - "references, but found: 'x(10) = MATMUL(x(10) * x(10), x(10) * " - "x(10))\n'." in str(excinfo.value)) + "references, but found: 'x(10) = MATMUL(matrix_a=x(10) * x(10), " + "matrix_b=x(10) * x(10))\n'." in str(excinfo.value)) def test_validate_arg_not_arr(): diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py index cee0b1b316..ea4dd44b79 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py @@ -68,7 +68,7 @@ def test_init_var(): trans = Maxval2LoopTrans() var_symbol = DataSymbol("var", REAL_TYPE) result = trans._init_var(Reference(var_symbol)) - assert result.debug_string() == "-HUGE(var)" + assert result.debug_string() == "-HUGE(x=var)" def test_str(): @@ -123,7 +123,7 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): " real :: result\n" " integer :: idx\n" " integer :: idx_1\n\n" - " result = -HUGE(result)\n" + " result = -HUGE(x=result)\n" " do idx = 1, 20, 1\n" " do idx_1 = 1, 10, 1\n" " result = MAX(result, array(idx_1,idx))\n" diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py index bf0225d991..6710ec45a1 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py @@ -68,7 +68,7 @@ def test_init_var(): trans = Minval2LoopTrans() var_symbol = DataSymbol("var", REAL_TYPE) result = trans._init_var(Reference(var_symbol)) - assert result.debug_string() == "HUGE(var)\n" + assert result.debug_string() == "HUGE(x=var)\n" def test_str(): @@ -116,7 +116,7 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): " result = minval(array)\n" "end subroutine\n") expected = ( - " result = HUGE(result)\n" + " result = HUGE(x=result)\n" " do idx = 1, 20, 1\n" " do idx_1 = 1, 10, 1\n" " result = MIN(result, array(idx_1,idx))\n" diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py index e2627cb139..5dded973fc 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py @@ -107,7 +107,7 @@ def test_correct(func, output, tmpdir): f" real, intent(inout) :: arg\n" f" real, intent(inout) :: arg_1\n" f" real :: psyir_tmp\n\n" - f" psyir_tmp = SIGN({output}, arg_1)\n\n" + f" psyir_tmp = SIGN(a={output}, b=arg_1)\n\n" f"end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -161,7 +161,7 @@ def test_correct_expr(tmpdir): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = 1.0 + SIGN(arg * 3.14, arg_1) + 2.0\n\n" + " psyir_tmp = 1.0 + SIGN(a=arg * 3.14, b=arg_1) + 2.0\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -215,7 +215,7 @@ def test_correct_2sign(tmpdir, fortran_writer): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = SIGN(1.0, 1.0) + SIGN(arg * 3.14, arg_1)\n\n" + " psyir_tmp = SIGN(a=1.0, b=1.0) + SIGN(a=arg * 3.14, b=arg_1)\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -271,7 +271,7 @@ def test_sign_with_integer_arg(fortran_reader, fortran_writer, tmpdir): program test_prog integer, parameter :: idef = kind(1) integer(idef) :: my_arg, other_arg - my_arg = SIGN(my_arg, other_arg) + my_arg = SIGN(a=my_arg, b=other_arg) end program test_prog''' psyir = fortran_reader.psyir_from_source(code) trans = Sign2CodeTrans() From 3956c10890cb463638f64426f0236c61680506bc Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 23 Sep 2025 13:18:36 +0100 Subject: [PATCH 04/50] More test updates --- src/psyclone/tests/psyad/tl2ad_test.py | 17 ++++++----- .../transformations/acc_kernels_trans_test.py | 30 ++++++++++--------- .../arrayassignment2loops_trans_test.py | 2 +- .../reference2arrayrange_trans_test.py | 6 ++-- .../replace_induction_variables_trans_test.py | 16 +++++----- .../scalarisation_trans_test.py | 10 +++---- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/psyclone/tests/psyad/tl2ad_test.py b/src/psyclone/tests/psyad/tl2ad_test.py index cbadc42e20..2be748f06c 100644 --- a/src/psyclone/tests/psyad/tl2ad_test.py +++ b/src/psyclone/tests/psyad/tl2ad_test.py @@ -701,6 +701,7 @@ def test_generate_adjoint_test(fortran_reader, fortran_writer): harness = fortran_writer(test_psyir) assert (" real, dimension(npts) :: field\n" " real, dimension(npts) :: field_input" in harness) + print(harness) assert (" call random_number(field)\n" " field_input = field\n" "\n" @@ -710,7 +711,8 @@ def test_generate_adjoint_test(fortran_reader, fortran_writer): " ! compute the inner product of the results of the tangent-" "linear kernel\n" " inner1 = 0.0\n" - " inner1 = inner1 + dot_product(field, field)\n" + " inner1 = inner1 + dot_product(vector_a=field, " + "vector_b=field)\n" "\n" " ! call the adjoint of the kernel\n" " call adj_kern(field, npts)\n" @@ -718,11 +720,12 @@ def test_generate_adjoint_test(fortran_reader, fortran_writer): " ! compute inner product of results of adjoint kernel with " "the original inputs to the tangent-linear kernel\n" " inner2 = 0.0\n" - " inner2 = inner2 + dot_product(field, field_input)\n" + " inner2 = inner2 + dot_product(vector_a=field, " + "vector_b=field_input)\n" "\n" " ! test the inner-product values for equality, allowing for " "the precision of the active variables\n" - " machinetol = spacing(max(abs(inner1), abs(inner2)))\n" + " machinetol = spacing(x=max(abs(a=inner1), abs(a=inner2)))\n" in harness.lower()) # Ideally we would test that the generated harness code compiles # but, since it depends on the TL and adjoint kernels, we can't @@ -1152,7 +1155,7 @@ def test_create_inner_product_1d_arrays(fortran_writer): assert isinstance(nodes[1].rhs, BinaryOperation) assert nodes[1].rhs.operator == BinaryOperation.Operator.ADD code = fortran_writer(nodes[1]) - assert "result = result + DOT_PRODUCT(var1, var2)" in code + assert "result = result + DOT_PRODUCT(vector_a=var1, vector_b=var2)" in code def test_create_inner_product_arrays(fortran_writer): @@ -1176,7 +1179,7 @@ def test_create_inner_product_arrays(fortran_writer): assert isinstance(nodes[1].rhs, BinaryOperation) assert nodes[1].rhs.operator == BinaryOperation.Operator.ADD code = fortran_writer(nodes[1]) - assert "result = result + SUM(var1(:,:,:) * var2(:,:,:))" in code + assert "result = result + SUM(array=var1(:,:,:) * var2(:,:,:))" in code def test_inner_product_scalars_and_arrays(fortran_writer): @@ -1201,7 +1204,7 @@ def test_inner_product_scalars_and_arrays(fortran_writer): assert all(isinstance(node, Assignment) for node in nodes) assert fortran_writer(nodes[0]) == "result = 0.0\n" assert (fortran_writer(nodes[1]) == - "result = result + SUM(var1(:,:,:) * var2(:,:,:))\n") + "result = result + SUM(array=var1(:,:,:) * var2(:,:,:))\n") assert (fortran_writer(nodes[2]) == - "result = result + DOT_PRODUCT(vec1, vec2)\n") + "result = result + DOT_PRODUCT(vector_a=vec1, vector_b=vec2)\n") assert fortran_writer(nodes[3]) == "result = result + a1 * a2\n" diff --git a/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py b/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py index f620dcc8e3..2d7bffa1ad 100644 --- a/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py @@ -278,12 +278,12 @@ def test_kernels_around_where_construct(fortran_reader, fortran_writer): assert isinstance(schedule[0], ACCKernelsDirective) assert isinstance(schedule[0].dir_body[0], Loop) assert (" !$acc kernels\n" - " do widx2 = 1, SIZE(a(:,:), dim=2), 1\n" - " do widx1 = 1, SIZE(a(:,:), dim=1), 1\n" - " if (a(LBOUND(a, dim=1) + widx1 - 1," - "LBOUND(a, dim=2) + widx2 - 1) < flag) then\n" - " b(LBOUND(b, dim=1) + widx1 - 1," - "LBOUND(b, dim=2) + widx2 - 1) = 0.0\n" + " do widx2 = 1, SIZE(array=a(:,:), dim=2), 1\n" + " do widx1 = 1, SIZE(array=a(:,:), dim=1), 1\n" + " if (a(LBOUND(array=a, dim=1) + widx1 - 1," + "LBOUND(array=a, dim=2) + widx2 - 1) < flag) then\n" + " b(LBOUND(array=b, dim=1) + widx1 - 1," + "LBOUND(array=b, dim=2) + widx2 - 1) = 0.0\n" " end if\n" " enddo\n" " enddo\n" @@ -303,14 +303,15 @@ def test_kernels_around_where_stmt(fortran_reader, fortran_writer): schedule = psyir.walk(Routine)[0] acc_trans = ACCKernelsTrans() acc_trans.apply([schedule[1]]) + print(fortran_writer(psyir)) assert (" a(:,:) = 1.0\n" " !$acc kernels\n" - " do widx2 = 1, SIZE(a(:,:), dim=2), 1\n" - " do widx1 = 1, SIZE(a(:,:), dim=1), 1\n" - " if (a(LBOUND(a, dim=1) + widx1 - 1," - "LBOUND(a, dim=2) + widx2 - 1) < flag) then\n" - " b(LBOUND(b, dim=1) + widx1 - 1," - "LBOUND(b, dim=2) + widx2 - 1) = 0.0\n" + " do widx2 = 1, SIZE(array=a(:,:), dim=2), 1\n" + " do widx1 = 1, SIZE(array=a(:,:), dim=1), 1\n" + " if (a(LBOUND(array=a, dim=1) + widx1 - 1," + "LBOUND(array=a, dim=2) + widx2 - 1) < flag) then\n" + " b(LBOUND(array=b, dim=1) + widx1 - 1," + "LBOUND(array=b, dim=2) + widx2 - 1) = 0.0\n" " end if\n" " enddo\n" " enddo\n" @@ -471,11 +472,12 @@ def test_no_assumed_size_char_in_kernels(fortran_reader): with pytest.raises(TransformationError) as err: acc_trans.validate(sub.children[1], options={"allow_string": True}) assert ("Assumed-size character variables cannot be enclosed in an OpenACC" - " region but found 'assumed_size_char(:LEN(explicit_size_char)) = " + " region but found 'assumed_size_char(:LEN(string=" + "explicit_size_char)) = " in str(err.value)) with pytest.raises(TransformationError) as err: acc_trans.validate(sub.children[2], options={"allow_string": True}) - assert ("Cannot include 'ACHAR(9)' in an OpenACC region because " + assert ("Cannot include 'ACHAR(i=9)' in an OpenACC region because " "it is not available on GPU" in str(err.value)) # Check that the character assignment is excluded by default. with pytest.raises(TransformationError) as err: diff --git a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py index 3b0d5bc906..63946b586f 100644 --- a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py @@ -99,7 +99,7 @@ def test_str(): ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y + size(y)\n", " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" - " x(idx) = y(idx) + SIZE(y)\n"), + " x(idx) = y(idx) + SIZE(array=y)\n"), # Mix different array ranks with fixed indices ("integer, dimension(:) :: x, z, t\n" diff --git a/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py b/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py index 4db965113f..2eb126fb5f 100644 --- a/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py @@ -165,7 +165,7 @@ def test_intrinsics(fortran_reader, fortran_writer): ''' code = CODE.replace("a = b", "b = dot_product(a, a(:))") result = apply_trans(fortran_reader, fortran_writer, code) - assert "b = DOT_PRODUCT(a, a(:))" in result + assert "b = DOT_PRODUCT(vector_a=a, vector_b=a(:))" in result def test_call(fortran_reader, fortran_writer): @@ -261,8 +261,8 @@ def test_validate_query(fortran_reader): with pytest.raises(TransformationError) as info: trans.validate(reference) assert (f"supplied node is passed as an argument to a Call to a " - f"non-elemental routine ({text}(a, 1)) and should not be " - f"transformed." in str(info.value)) + f"non-elemental routine ({text}(array=a, dim=1)) and " + f"should not be transformed." in str(info.value)) # Check the references to 'b' in the hidden lbound and ubound # intrinsics within 'b(:)' do not get modified. diff --git a/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py b/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py index 4e7cd9aa7a..46292eff24 100644 --- a/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py @@ -122,14 +122,14 @@ def test_riv_working(fortran_reader, fortran_writer): "(i + 1 + invariant)" in out_loop) assert "ic4 = i - 1 + 1 + invariant" in out_all - assert ("a(i * i + 3 * SIN(i)) = 5 + (i * i + 3 * SIN(i) + 1) * " - "(i * i + 3 * SIN(i))" in out_loop) - assert "ic5 = (i - 1) * (i - 1) + 3 * SIN(i - 1)" in out_all - assert ("a(i + 1 + invariant + (i * i + 3 * SIN(i))) = 6 + " - "(i + 1 + invariant + (i * i + 3 * SIN(i)) + 1) * " - "(i + 1 + invariant + (i * i + 3 * SIN(i)))" in out_loop) + assert ("a(i * i + 3 * SIN(x=i)) = 5 + (i * i + 3 * SIN(x=i) + 1) * " + "(i * i + 3 * SIN(x=i))" in out_loop) + assert "ic5 = (i - 1) * (i - 1) + 3 * SIN(x=i - 1)" in out_all + assert ("a(i + 1 + invariant + (i * i + 3 * SIN(x=i))) = 6 + " + "(i + 1 + invariant + (i * i + 3 * SIN(x=i)) + 1) * " + "(i + 1 + invariant + (i * i + 3 * SIN(x=i)))" in out_loop) assert ("ic6 = i - 1 + 1 + invariant + ((i - 1) * (i - 1) + " - "3 * SIN(i - 1))" in out_all) + "3 * SIN(x=i - 1))" in out_all) assert "a(13) = 7 + (13 + 1) * 13" in out_loop # Make sure the assignment to t1%a has been added outside of the loop: assert "t1%a = 13" in out_all @@ -255,6 +255,6 @@ def test_riv_impure_function_calls(fortran_reader, fortran_writer): out = fortran_writer(loop) # ic1 has been replaced - assert "a(SIN(i)) = 1 + (SIN(i) + 1) * SIN(i)" in out + assert "a(SIN(x=i)) = 1 + (SIN(x=i) + 1) * SIN(x=i)" in out # ic2 has NOT been replaced assert "a(ic2) = 1 + (ic2 + 1) * ic2" in out diff --git a/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py b/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py index 5420028448..565f54bca9 100644 --- a/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py @@ -625,7 +625,7 @@ def test_scalarisation_trans_apply(fortran_reader, fortran_writer, tmpdir): do i = 1, 100, 1 arr_scalar = i - arr_scalar = EXP(arr_scalar) + arr_scalar = EXP(x=arr_scalar) k = i b(i) = arr_scalar * 3 c(k) = i @@ -648,7 +648,7 @@ def test_scalarisation_trans_apply(fortran_reader, fortran_writer, tmpdir): do i = 1, 100 arr(i) = i - arr(i) = exp(arr(i)) + arr(i) = exp(x=arr(i)) k = i b(i) = arr(i) * 3 c(k) = i @@ -677,7 +677,7 @@ def test_scalarisation_trans_apply(fortran_reader, fortran_writer, tmpdir): do i = 1, 100, 1 arr_scalar = i - arr_scalar = EXP(arr_scalar) + arr_scalar = EXP(x=arr_scalar) k = i b(i) = arr_scalar * 3 c(k) = i @@ -703,7 +703,7 @@ def test_scalarisation_trans_apply_routinesymbol(fortran_reader, integer :: i integer, allocatable, dimension(:,:,:) :: k do i= 1, 100 - allocate(k(MAXVAL(j(1:3)),1,1)) + allocate(k(MAXVAL(array=j(1:3)),1,1)) deallocate(k) end do end subroutine test''' @@ -716,7 +716,7 @@ def test_scalarisation_trans_apply_routinesymbol(fortran_reader, integer, allocatable, dimension(:,:,:) :: k do i = 1, 100, 1 - ALLOCATE(k(1:MAXVAL(j(:)),1:1,1:1)) + ALLOCATE(k(1:MAXVAL(array=j(:)),1:1,1:1)) DEALLOCATE(k) enddo From fed2ccc166fb2aeae5e2e9218055a52507f0ebc9 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 23 Sep 2025 13:19:34 +0100 Subject: [PATCH 05/50] linting --- src/psyclone/tests/psyad/tl2ad_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psyclone/tests/psyad/tl2ad_test.py b/src/psyclone/tests/psyad/tl2ad_test.py index 2be748f06c..12bcd57265 100644 --- a/src/psyclone/tests/psyad/tl2ad_test.py +++ b/src/psyclone/tests/psyad/tl2ad_test.py @@ -1155,7 +1155,8 @@ def test_create_inner_product_1d_arrays(fortran_writer): assert isinstance(nodes[1].rhs, BinaryOperation) assert nodes[1].rhs.operator == BinaryOperation.Operator.ADD code = fortran_writer(nodes[1]) - assert "result = result + DOT_PRODUCT(vector_a=var1, vector_b=var2)" in code + assert ("result = result + DOT_PRODUCT(vector_a=var1, vector_b=var2)" + in code) def test_create_inner_product_arrays(fortran_writer): From 19a587748030d0bcb1dcff8d2a897abacb4d545e Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 23 Sep 2025 17:27:01 +0100 Subject: [PATCH 06/50] All but 2 tests now pass, both have behaviour changes to fix --- src/psyclone/psyir/backend/visitor.py | 1 - .../tests/core/symbolic_maths_test.py | 4 +- .../kernel_module_inline_trans_test.py | 2 +- .../gocean_opencl_trans_test.py | 59 +++++----- .../tests/domain/lfric/lfric_builtins_test.py | 22 ++-- .../nemo/transformations/acc_update_test.py | 4 +- .../tests/psyad/adjoint_visitor_test.py | 7 +- .../psyad/domain/common/test_adjoint_utils.py | 8 +- .../lfric/test_lfric_adjoint_harness.py | 4 +- src/psyclone/tests/psyad/main_test.py | 4 +- .../transformations/test_assignment_trans.py | 6 +- .../psyad/transformations/test_preprocess.py | 10 +- .../tests/psyir/backend/fortran_test.py | 46 ++++---- .../fparser2_intrinsic_handler_test.py | 104 +++++++++++------- .../tests/psyir/frontend/fparser2_test.py | 4 +- .../frontend/fparser2_where_handler_test.py | 58 +++++----- .../tests/psyir/nodes/array_mixin_test.py | 25 +++-- .../nodes/dynamic_omp_task_directive_test.py | 13 ++- .../tests/psyir/nodes/intrinsic_call_test.py | 20 ++-- .../nodes/type_convert_intrinsic_test.py | 6 +- .../hoist_local_arrays_trans_test.py | 36 +++--- .../hoist_loop_bound_expr_trans_test.py | 8 +- .../psyir/transformations/hoist_trans_test.py | 4 +- .../array_reduction_base_trans_test.py | 10 +- .../intrinsics/dotproduct2code_trans_test.py | 18 +-- .../intrinsics/sign2code_trans_test.py | 4 +- .../omp_task_transformations_test.py | 4 +- 27 files changed, 266 insertions(+), 225 deletions(-) diff --git a/src/psyclone/psyir/backend/visitor.py b/src/psyclone/psyir/backend/visitor.py index a42a6daaa7..bd9cc36483 100644 --- a/src/psyclone/psyir/backend/visitor.py +++ b/src/psyclone/psyir/backend/visitor.py @@ -248,7 +248,6 @@ def _visit(self, node): possible_method_names = [curr_class.__name__.lower()+"_node" for curr_class in inspect.getmro(type(node))] possible_method_names.remove("object_node") - # Try to call methods using the class names in the order of # the class hierarchy (starting from the current class name). for method_name in possible_method_names: diff --git a/src/psyclone/tests/core/symbolic_maths_test.py b/src/psyclone/tests/core/symbolic_maths_test.py index 4b3cb3be91..899aef625a 100644 --- a/src/psyclone/tests/core/symbolic_maths_test.py +++ b/src/psyclone/tests/core/symbolic_maths_test.py @@ -480,7 +480,7 @@ def test_symbolic_math_use_range(fortran_reader, expressions): # not it is a scalar. ("a / a(i)", "a / a(i)"), ("norm_u(idx+iw2) * u_e(idx + (LBOUND(u_e,dim=1)-iw2v), df2)", - "norm_u(idx + iw2) * u_e(idx - iw2v + LBOUND(u_e, 1),df2)")]) + "norm_u(idx + iw2) * u_e(idx - iw2v + LBOUND(array=u_e, dim=1),df2)")]) def test_symbolic_maths_expand(fortran_reader, fortran_writer, expr, expected): '''Test the expand method works as expected.''' # A dummy program to easily create the PSyIR for the @@ -526,7 +526,7 @@ def test_expand_with_intrinsic(fortran_reader, fortran_writer): sym_maths.expand(rhs) result = fortran_writer(psyir).lower() # Check that the 'u_e' argument remains unchanged. - assert "lbound(u_e, 1),df2)" in result + assert "lbound(array=u_e, dim=1),df2)" in result def test_symbolic_maths_expand_function(fortran_reader, fortran_writer): diff --git a/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py b/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py index 61d67c2cf0..c21aac3a80 100644 --- a/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py +++ b/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py @@ -92,7 +92,7 @@ def test_validate_inline_error_if_not_kernel(fortran_reader): call = psyir.walk(Call)[0] with pytest.raises(TransformationError) as err: inline_trans.apply(call) - assert ("Cannot module-inline a call to an intrinsic (got 'SIN(b)')" + assert ("Cannot module-inline a call to an intrinsic (got 'SIN(x=b)')" in str(err.value)) diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py index ccf2af705f..8968645f48 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py @@ -316,18 +316,18 @@ def test_invoke_opencl_initialisation_grid(): integer(kind=c_size_t) :: size_in_bytes if (.not.c_associated(field%grid%tmask_device)) then - size_in_bytes = int(field%grid%nx * field%grid%ny, 8) * \ + size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%tmask(1,1)) - field%grid%tmask_device = transfer(create_ronly_buffer(size_in_bytes),\ - field%grid%tmask_device) - size_in_bytes = int(field%grid%nx * field%grid%ny, 8) * \ + field%grid%tmask_device = transfer(source=create_ronly_buffer(\ +size_in_bytes), mold=field%grid%tmask_device) + size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%''' assert expected in generated_code for grid_property in check_properties: code = (f"field%grid%{grid_property}_device = transfer(" - f"create_ronly_buffer(size_in_bytes), " - f"field%grid%{grid_property}_device)") + f"source=create_ronly_buffer(size_in_bytes), " + f"mold=field%grid%{grid_property}_device)") assert code in generated_code # Check that device grid write routine is generated @@ -344,19 +344,19 @@ def test_invoke_opencl_initialisation_grid(): integer :: ierr cmd_queues => get_cmd_queues() - size_in_bytes = int(field%grid%nx * field%grid%ny, 8) * \ + size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%tmask(1,1)) - cl_mem = transfer(field%grid%tmask_device, cl_mem) + cl_mem = transfer(source=field%grid%tmask_device, mold=cl_mem) ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem,cl_true,0_8,\ size_in_bytes,c_loc(field%grid%tmask),0,c_null_ptr,c_null_ptr) call check_status('clenqueuewritebuffer tmask', ierr) - size_in_bytes = int(field%grid%nx * field%grid%ny, 8) * \ + size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%area_t(1,1))''' assert expected in generated_code for grid_property in check_properties: - code = (f" cl_mem = transfer(field%grid%{grid_property}_device, " - f"cl_mem)\n" + code = (f" cl_mem = transfer(source=field%grid%{grid_property}_" + f"device, mold=cl_mem)\n" f" ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem," f"cl_true,0_8,size_in_bytes,c_loc(field%grid%{grid_property})," f"0,c_null_ptr,c_null_ptr)\n" @@ -397,7 +397,6 @@ def test_invoke_opencl_initialisation_grid(): ystop - 1) ! write data to the device''' - print(generated_code) assert expected in generated_code # The write_to_device() can appear in any order in the following 5 lines @@ -451,12 +450,12 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer :: ierr integer :: i - cl_mem = transfer(from, cl_mem) + cl_mem = transfer(source=from, mold=cl_mem) cmd_queues => get_cmd_queues() - if (nx < size(to, 1) / 2) then + if (nx < size(array=to, dim=1) / 2) then do i = starty, starty + ny, 1 - size_in_bytes = int(nx, 8) * c_sizeof(to(1,1)) - offset_in_bytes = int(size(to, 1) * (i - 1) + \ + size_in_bytes = int(a=nx, kind=8) * c_sizeof(to(1,1)) + offset_in_bytes = int(a=size(array=to, dim=1) * (i - 1) + \ (startx - 1)) * c_sizeof(to(1,1)) ierr = clenqueuereadbuffer(cmd_queues(1),cl_mem,cl_false,\ offset_in_bytes,size_in_bytes,c_loc(to(startx,i)),0,c_null_ptr,c_null_ptr) @@ -466,9 +465,10 @@ def test_opencl_routines_initialisation(kernel_outputdir): call check_status('clfinish on read', clfinish(cmd_queues(1))) end if else - size_in_bytes = int(size(to, 1) * ny, 8) * c_sizeof(to(1,1)) - offset_in_bytes = int(size(to, 1) * (starty - 1), 8) * \ + size_in_bytes = int(a=size(array=to, dim=1) * ny, kind=8) * \ c_sizeof(to(1,1)) + offset_in_bytes = int(a=size(array=to, dim=1) * (starty - 1), kind=8) \ +* c_sizeof(to(1,1)) ierr = clenqueuereadbuffer(cmd_queues(1),cl_mem,cl_true,\ offset_in_bytes,size_in_bytes,c_loc(to(1,starty)),0,c_null_ptr,c_null_ptr) call check_status('clenqueuereadbuffer', ierr) @@ -499,13 +499,13 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer :: ierr integer :: i - cl_mem = transfer(to, cl_mem) + cl_mem = transfer(source=to, mold=cl_mem) cmd_queues => get_cmd_queues() - if (nx < size(from, 1) / 2) then + if (nx < size(array=from, dim=1) / 2) then do i = starty, starty + ny, 1 - size_in_bytes = int(nx, 8) * c_sizeof(from(1,1)) - offset_in_bytes = int(size(from, 1) * (i - 1) + (startx - 1)) * \ -c_sizeof(from(1,1)) + size_in_bytes = int(a=nx, kind=8) * c_sizeof(from(1,1)) + offset_in_bytes = int(a=size(array=from, dim=1) * (i - 1) + \ +(startx - 1)) * c_sizeof(from(1,1)) ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem,cl_false,\ offset_in_bytes,size_in_bytes,c_loc(from(startx,i)),0,c_null_ptr,c_null_ptr) call check_status('clenqueuewritebuffer', ierr) @@ -514,8 +514,9 @@ def test_opencl_routines_initialisation(kernel_outputdir): call check_status('clfinish on write', clfinish(cmd_queues(1))) end if else - size_in_bytes = int(size(from, 1) * ny, 8) * c_sizeof(from(1,1)) - offset_in_bytes = int(size(from, 1) * (starty - 1)) * \ + size_in_bytes = int(a=size(array=from, dim=1) * ny, kind=8) * \ +c_sizeof(from(1,1)) + offset_in_bytes = int(a=size(array=from, dim=1) * (starty - 1)) * \ c_sizeof(from(1,1)) ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem,cl_true,\ offset_in_bytes,size_in_bytes,c_loc(from(1,starty)),0,c_null_ptr,c_null_ptr) @@ -535,10 +536,10 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer(kind=c_size_t) :: size_in_bytes if (.not.field%data_on_device) then - size_in_bytes = int(field%grid%nx * field%grid%ny, 8) * \ + size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%data(1,1)) - field%device_ptr = transfer(create_rw_buffer(size_in_bytes), \ -field%device_ptr) + field%device_ptr = transfer(source=create_rw_buffer(size_in_bytes), \ +mold=field%device_ptr) field%data_on_device = .true. field%read_from_device_f => read_from_device field%write_to_device_f => write_to_device @@ -650,7 +651,7 @@ def test_psy_init_multiple_devices_per_node(kernel_outputdir, monkeypatch): if (.not.initialised) then initialised = .true. - ocl_device_num = mod(get_rank() - 1, 2) + 1 + ocl_device_num = mod(a=get_rank() - 1, p=2) + 1 call ocl_env_init(1, ocl_device_num, .false., .false.) kernel_names(1) = 'compute_cu_code' call add_kernels(1, kernel_names) diff --git a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py index 41a9bcce0a..6f38675f48 100644 --- a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py @@ -1628,7 +1628,7 @@ def test_setval_random(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: setval_random (fill a real-valued field " "with pseudo-random numbers)\n" - "call RANDOM_NUMBER(f1_data(df))\n") in code + "call RANDOM_NUMBER(harvest=f1_data(df))\n") in code # ------------- Sign of real field elements --------------------------------- # @@ -1654,7 +1654,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(a, f1_data(df))\n") in code + "f2_data(df) = SIGN(a=a, b=f1_data(df))\n") in code # Also with a literal kern = builtin_from_file("15.10.2_sign_X_builtin_set_by_value.f90") @@ -1665,7 +1665,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(-2.0_r_def, f1_data(df))\n" in code) + "f2_data(df) = SIGN(a=-2.0_r_def, b=f1_data(df))\n" in code) # The integer version has the datatype changed to integer in the metadata # and string representation @@ -1917,7 +1917,7 @@ def test_int_to_real_x(fortran_writer): assert ( "! Built-in: int_to_real_X (convert an integer-valued to a " "real-valued field)\n" - "f2_data(df) = REAL(f1_data(df), kind=r_def)\n") in code + "f2_data(df) = REAL(a=f1_data(df), kind=r_def)\n") in code @pytest.mark.parametrize("kind_name", ["r_solver", "r_tran", "r_bl"]) @@ -1959,7 +1959,7 @@ def test_int_to_real_x_precision(tmpdir, kind_name): assert (f"real(kind={kind_name}), pointer, dimension(:) :: " "f2_data => null()") in code assert f"type({kind_name}_field_proxy_type) :: f2_proxy" in code - assert f"f2_data(df) = REAL(f1_data(df), kind={kind_name})" in code + assert f"f2_data(df) = REAL(a=f1_data(df), kind={kind_name})" in code # Test compilation of generated code assert LFRicBuild(tmpdir).code_compiles(psy) @@ -1986,7 +1986,7 @@ def test_real_to_int_x(fortran_writer): assert ( "! Built-in: real_to_int_X (convert a real-valued to an " "integer-valued field)\n" - "f2_data(df) = INT(f1_data(df), kind=i_def)\n") in code + "f2_data(df) = INT(a=f1_data(df), kind=i_def)\n") in code @pytest.mark.parametrize("kind_name", ["i_um", "i_ncdf"]) @@ -2017,7 +2017,7 @@ def test_real_to_int_x_precision(monkeypatch, tmpdir, kind_name): assert "use constants_mod\n" in code assert ("integer(kind=i_def), pointer, dimension(:) :: f2_data => null()" in code) - assert f"f2_data(df) = INT(f1_data(df), kind={kind_name})" in code + assert f"f2_data(df) = INT(a=f1_data(df), kind={kind_name})" in code # Test compilation of generated code assert LFRicBuild(tmpdir).code_compiles(psy) @@ -2048,17 +2048,17 @@ def test_real_to_real_x(fortran_writer): assert ( "! Built-in: real_to_real_X (convert a real-valued to a " "real-valued field)\n" - "f2_data(df) = REAL(f1_data(df), kind=r_tran)\n") in code + "f2_data(df) = REAL(a=f1_data(df), kind=r_tran)\n") in code elif idx == 1: assert ( "! Built-in: real_to_real_X (convert a real-valued to a " "real-valued field)\n" - "f1_data(df) = REAL(f3_data(df), kind=r_def)\n") in code + "f1_data(df) = REAL(a=f3_data(df), kind=r_def)\n") in code elif idx == 2: assert ( "! Built-in: real_to_real_X (convert a real-valued to a " "real-valued field)\n" - "f3_data(df) = REAL(f2_data(df), kind=r_solver)\n") in code + "f3_data(df) = REAL(a=f2_data(df), kind=r_solver)\n") in code else: assert False, code # There are only 3 kern @@ -2089,7 +2089,7 @@ def test_real_to_real_x_lowering(monkeypatch, tmpdir, kind_name): assert "use constants_mod\n" in code # Assert correct type is set - assert f"f2_data(df) = REAL(f1_data(df), kind={kind_name})" in code + assert f"f2_data(df) = REAL(a=f1_data(df), kind={kind_name})" in code # Test compilation of generated code assert LFRicBuild(tmpdir).code_compiles(psy) diff --git a/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py b/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py index bc6f6cc843..2404799798 100644 --- a/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py +++ b/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py @@ -215,7 +215,7 @@ def test_call_accesses(fortran_reader, fortran_writer): " !$acc update if_present device(zftv)\n" " call dia_ptr_hst(jn, 'ldf', zftv(:,:,:))\n" " !$acc update if_present host(checksum,zftv)\n" - " checksum = SUM(zftv)\n" + " checksum = SUM(array=zftv)\n" " !$acc update if_present device(checksum,jn,zftv)\n" in code) @@ -247,7 +247,7 @@ def test_call_within_if(fortran_reader, fortran_writer): " !$acc update if_present device(zftv)\n" in code) assert (" end if\n" " !$acc update if_present host(checksum,zftv)\n" - " checksum = SUM(zftv)\n" + " checksum = SUM(array=zftv)\n" " !$acc update if_present device(checksum)\n" in code) diff --git a/src/psyclone/tests/psyad/adjoint_visitor_test.py b/src/psyclone/tests/psyad/adjoint_visitor_test.py index 197890e9cc..5fd031debb 100644 --- a/src/psyclone/tests/psyad/adjoint_visitor_test.py +++ b/src/psyclone/tests/psyad/adjoint_visitor_test.py @@ -640,8 +640,9 @@ def test_loop_node_bounds_intrinsic(fortran_reader, fortran_writer, tmpdir): " integer :: i\n\n" " a = 0.0\n" " b = 0.0\n" - " do i = UBOUND(a, 1) - MOD(UBOUND(a, 1) - LBOUND(a, 1), " - "2 * UBOUND(b, 1)), LBOUND(a, 1), -1 * (2 * UBOUND(b, 1))\n" + " do i = UBOUND(array=a, dim=1) - MOD(UBOUND(array = a, dim = 1) " + "- LBOUND(array = a, dim = 1), 2 * UBOUND(array = b, dim = 1)), " + "LBOUND(array=a, dim=1), -1 * (2 * UBOUND(array=b, dim=1))\n" " a(i) = 0.0\n" " enddo\n\n" "end program test\n") @@ -666,7 +667,7 @@ def test_loop_node_bounds_intrinsic(fortran_reader, fortran_writer, tmpdir): with pytest.raises(VisitorError) as error: _ = adj_visitor(tl_psyir) assert ("The upper bound of a loop should not contain active variables, " - "but found 'b' in 'UBOUND(a, b)'." in str(error.value)) + "but found 'b' in 'UBOUND(array=a, dim=b)'." in str(error.value)) def test_loop_node_passive(fortran_reader): diff --git a/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py b/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py index 51dda378fc..38168bbd61 100644 --- a/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py +++ b/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py @@ -87,8 +87,8 @@ def test_create_real_comparison(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" - " relative_diff = ABS(var1 - var2) / MachineTol\n" + " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" + " relative_diff = ABS(a=var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" " ! - Unsupported statement: Write_Stmt\n" @@ -125,8 +125,8 @@ def test_common_real_comparison(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" - " relative_diff = ABS(var1 - var2) / MachineTol\n") + " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" + " relative_diff = ABS(a=var1 - var2) / MachineTol\n") assert expected in result # _common_write diff --git a/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py b/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py index 2543f07adb..2b0f2f34f1 100644 --- a/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py +++ b/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py @@ -457,7 +457,7 @@ def test_lfric_create_real_comparison(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" + " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" " relative_diff = ABS(a=var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" @@ -499,7 +499,7 @@ def test_lfric_log_write(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" + " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" " relative_diff = ABS(a=var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" diff --git a/src/psyclone/tests/psyad/main_test.py b/src/psyclone/tests/psyad/main_test.py index 32bcaea41e..2618998479 100644 --- a/src/psyclone/tests/psyad/main_test.py +++ b/src/psyclone/tests/psyad/main_test.py @@ -150,7 +150,7 @@ ! test the inner-product values for equality, allowing for the precision \ of the active variables - machinetol = spacing(max(abs(inner1), abs(inner2))) + machinetol = spacing(x=max(abs(a=inner1), abs(a=inner2))) relative_diff = abs(a=inner1 - inner2) / machinetol if (relative_diff < overall_tolerance) then ! psyclone codeblock (unsupported code) reason: @@ -534,8 +534,6 @@ def test_main_otest_option(tmpdir, capsys, extra_args): assert output == "" with open(harness_out, 'r', encoding='utf-8') as my_file: data = my_file.read() - print(EXPECTED_HARNESS_CODE) - print(data.lower()) assert EXPECTED_HARNESS_CODE in data.lower() diff --git a/src/psyclone/tests/psyad/transformations/test_assignment_trans.py b/src/psyclone/tests/psyad/transformations/test_assignment_trans.py index 4fd8acdb03..a46fa82ec3 100644 --- a/src/psyclone/tests/psyad/transformations/test_assignment_trans.py +++ b/src/psyclone/tests/psyad/transformations/test_assignment_trans.py @@ -1282,9 +1282,9 @@ def test_validate_unaryop(): lhs_symbol, rhs_symbol]) with pytest.raises(TangentLinearError) as info: trans.validate(assignment) - assert ("Each term on the RHS of the assignment 'a = SQRT(b)\n' must be " - "linear with respect to the active variable, but found 'SQRT(b)'." - in str(info.value)) + assert ("Each term on the RHS of the assignment 'a = SQRT(x=b)\n' must " + "be linear with respect to the active variable, but found " + "'SQRT(x=b)'." in str(info.value)) def test_validate_mismatched_array_ranges(): diff --git a/src/psyclone/tests/psyad/transformations/test_preprocess.py b/src/psyclone/tests/psyad/transformations/test_preprocess.py index 7ea5243c47..1f0f91e266 100644 --- a/src/psyclone/tests/psyad/transformations/test_preprocess.py +++ b/src/psyclone/tests/psyad/transformations/test_preprocess.py @@ -101,7 +101,7 @@ def test_preprocess_reference2arrayrange(tmpdir, fortran_reader, " a(idx_1,idx) = b(idx_1,idx) * c(idx_1,idx)\n" " enddo\n" " enddo\n" - " do i = LBOUND(d, 1), UBOUND(d, 1), 1\n" + " do i = LBOUND(array=d, dim=1), UBOUND(array=d, dim=1), 1\n" " d(i) = 0.0\n" " enddo\n" " e(:,:) = f(:,:)\n\n" @@ -203,8 +203,8 @@ def test_preprocess_arrayassign2loop(tmpdir, fortran_reader, fortran_writer): expected = ( " integer :: idx\n" " integer :: idx_1\n\n" - " do idx = LBOUND(a, dim=3), UBOUND(a, dim=3), 1\n" - " do idx_1 = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" + " do idx = LBOUND(array=a, dim=3), UBOUND(array=a, dim=3), 1\n" + " do idx_1 = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" " a(idx_1,1,idx) = b(idx_1,1,idx) * c(idx_1,map(i) + 1,idx)\n" " enddo\n" " enddo\n" @@ -341,7 +341,7 @@ def test_preprocess_associativity4(fortran_reader, fortran_writer): " integer :: b\n" " integer, dimension(10) :: c\n" " integer, dimension(10) :: d\n\n" - " a = b * SUM(c(:)) + b * SUM(d(:))\n\n" + " a = b * SUM(array=c(:)) + b * SUM(array=d(:))\n\n" "end program test\n") psyir = fortran_reader.psyir_from_source(code) preprocess_trans(psyir, ["a", "c", "d"]) @@ -417,7 +417,7 @@ def test_associativity7(fortran_reader, fortran_writer): " integer, dimension(10) :: c\n" " integer, dimension(10) :: d\n" " type(my_type) :: mt\n\n" - " a = b * SUM(c(:)) + b * SUM(mt%x(:))\n\n" + " a = b * SUM(array=c(:)) + b * SUM(array=mt%x(:))\n\n" "end program test\n") psyir = fortran_reader.psyir_from_source(code) preprocess_trans(psyir, ["a", "c", "mt"]) diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index d1a894415b..8a7f1c45d7 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -689,7 +689,7 @@ def test_fw_gen_vardecl(fortran_writer): symbol = DataSymbol("dummy3i", INTEGER_TYPE, is_constant=True, initial_value=initval) result = fortran_writer.gen_vardecl(symbol) - assert result == "integer, parameter :: dummy3i = SIN(10)\n" + assert result == "integer, parameter :: dummy3i = SIN(x=10)\n" # Symbol has initial value but is not constant (static). This is a property # of the Fortran language and therefore is only checked for when we attempt @@ -1185,9 +1185,9 @@ def test_fw_mixed_operator_precedence(fortran_reader, fortran_writer, tmpdir): " a = -a + (-b + (-c))\n" " a = -a + (-b - (-c))\n" " b = c * (-2.0)\n" - " a = ABS(-b - (-c))\n" + " a = ABS(a=-b - (-c))\n" " e = .NOT.f .OR. (.NOT.g)\n" - " a = LOG(b * c)\n" + " a = LOG(x=b * c)\n" " a = b ** (-c)\n" " a = b ** (-b + c)\n" " a = (-b) ** c\n" @@ -1289,9 +1289,9 @@ def test_fw_range(fortran_writer): range2 = Range.create(dim2_bound_start, plus, step=three) # Check the ranges in isolation result = fortran_writer(range1) - assert result == "1:UBOUND(a, dim=1)" + assert result == "1:UBOUND(array=a, dim=1)" result = fortran_writer(range2) - assert result == "LBOUND(a, dim=2):b + c:3" + assert result == "LBOUND(array=a, dim=2):b + c:3" # Check the ranges in context array = ArrayReference.create( symbol, [range1, range2]) @@ -1326,8 +1326,8 @@ def test_fw_range(fortran_writer): Range.create(dim3_bound_stop.copy(), dim3_bound_start.copy(), step=three.copy())]) result = fortran_writer.arrayreference_node(array) - assert result == ("a(LBOUND(b, dim=1):UBOUND(b, dim=1),:2:3," - "UBOUND(a, dim=3):LBOUND(a, dim=3):3)") + assert result == ("a(LBOUND(array=b, dim=1):UBOUND(array=b, dim=1),:2:3," + "UBOUND(array=a, dim=3):LBOUND(array=a, dim=3):3)") def test_fw_range_structureref(fortran_writer): @@ -1363,8 +1363,8 @@ def test_fw_range_structureref(fortran_writer): ArrayOfStructuresReference.create(array_symbol, [range2], [("data", [range2.copy()])])) assert (result == - "my_grids(:)%data(LBOUND(my_grids, dim=1):" - "UBOUND(my_grids, dim=1))") + "my_grids(:)%data(LBOUND(array=my_grids, dim=1):" + "UBOUND(array=my_grids, dim=1))") symbol = DataSymbol("field", UnresolvedType()) int_one = Literal("1", INTEGER_TYPE) @@ -1382,9 +1382,10 @@ def test_fw_range_structureref(fortran_writer): ("second", [my_range.copy()])]) result = fortran_writer(ref) assert (result == - "field(LBOUND(field%first, dim=1):" - "UBOUND(field%first, dim=1))%first%second(" - "LBOUND(field%first, dim=1):UBOUND(field%first, dim=1))") + "field(LBOUND(array=field%first, dim=1):" + "UBOUND(array=field%first, dim=1))%first%second(" + "LBOUND(array=field%first, dim=1):" + "UBOUND(array=field%first, dim=1))") data_ref = Reference(array_symbol) start = IntrinsicCall.create( @@ -1516,7 +1517,7 @@ def test_fw_unaryoperator2(fortran_reader, fortran_writer, tmpdir): # Generate Fortran from the PSyIR schedule result = fortran_writer(schedule) - assert "a = SIN(1.0)" in result + assert "a = SIN(x=1.0)" in result assert Compile(tmpdir).string_compiles(result) @@ -1655,9 +1656,9 @@ def test_fw_query_intrinsics(fortran_reader, fortran_writer, tmpdir): # Generate Fortran from the PSyIR result = fortran_writer(psyir).lower() - assert "mysize = size(a, 2)" in result - assert "lb = lbound(a, 2)" in result - assert "ub = ubound(a, 2)" in result + assert "mysize = size(array=a, dim=2)" in result + assert "lb = lbound(array=a, dim=2)" in result + assert "ub = ubound(array=a, dim=2)" in result assert Compile(tmpdir).string_compiles(result) @@ -1861,18 +1862,23 @@ def test_fw_intrinsic_call_node(fortran_writer): rcall = IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, [Reference(sym)]) gen = fortran_writer(rcall) - assert gen == "call RANDOM_NUMBER(var)\n" + assert gen == "call RANDOM_NUMBER(harvest=var)\n" for intrinsic_function in [IntrinsicCall.Intrinsic.MINVAL, IntrinsicCall.Intrinsic.MAXVAL, - IntrinsicCall.Intrinsic.SUM, - IntrinsicCall.Intrinsic.TINY, + IntrinsicCall.Intrinsic.SUM]: + intrinsic_call = IntrinsicCall.create( + intrinsic_function, [Reference(sym)]) + assignment = Assignment.create(Reference(sym), intrinsic_call) + gen = fortran_writer(assignment) + assert gen == f"var = {intrinsic_function.name}(array=var)\n" + for intrinsic_function in [IntrinsicCall.Intrinsic.TINY, IntrinsicCall.Intrinsic.HUGE]: intrinsic_call = IntrinsicCall.create( intrinsic_function, [Reference(sym)]) assignment = Assignment.create(Reference(sym), intrinsic_call) gen = fortran_writer(assignment) - assert gen == f"var = {intrinsic_function.name}(var)\n" + assert gen == f"var = {intrinsic_function.name}(x=var)\n" def test_fw_comments(fortran_writer): diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index aeeb020476..786a07d783 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -196,7 +196,7 @@ def test_intrinsic_handler_intrinsiccall_mms( intrinsic_call = psyir.children[0].children[0].children[1] assert isinstance(intrinsic_call, IntrinsicCall) result = fortran_writer(intrinsic_call) - assert result == f"{intrinsic_name}(a, dim=d, mask=m)" + assert result == f"{intrinsic_name}(array=a, dim=d, mask=m)" routine_symbol = intrinsic_call.routine.symbol assert isinstance(routine_symbol, RoutineSymbol) @@ -230,7 +230,7 @@ def test_intrinsic_handler_intrinsiccall_onearg( intrinsic_call = psyir.children[0].children[0].children[1] assert isinstance(intrinsic_call, IntrinsicCall) result = fortran_writer(intrinsic_call) - assert result == f"{intrinsic_name}(a)" + assert result == f"{intrinsic_name}(x=a)" routine_symbol = intrinsic_call.routine.symbol assert isinstance(routine_symbol, RoutineSymbol) assert intrinsic_call.routine.name == intrinsic_name @@ -242,36 +242,64 @@ def test_intrinsic_handler_intrinsiccall_onearg( @pytest.mark.parametrize( - "code, expected_intrinsic", - [('x = exp(a)', IntrinsicCall.Intrinsic.EXP), - ('x = sin(a)', IntrinsicCall.Intrinsic.SIN), - ('x = asin(a)', IntrinsicCall.Intrinsic.ASIN), - ('idx = ceiling(a)', IntrinsicCall.Intrinsic.CEILING), - ('x = abs(a)', IntrinsicCall.Intrinsic.ABS), - ('x = cos(a)', IntrinsicCall.Intrinsic.COS), - ('x = acos(a)', IntrinsicCall.Intrinsic.ACOS), - ('x = tan(a)', IntrinsicCall.Intrinsic.TAN), - ('x = atan(a)', IntrinsicCall.Intrinsic.ATAN), - ('x = real(a)', IntrinsicCall.Intrinsic.REAL), - ('x = real(a, 8)', IntrinsicCall.Intrinsic.REAL), - ('x = int(a)', IntrinsicCall.Intrinsic.INT), - ('x = int(a, 8)', IntrinsicCall.Intrinsic.INT), - ('x = log(a)', IntrinsicCall.Intrinsic.LOG), - ('x = log10(a)', IntrinsicCall.Intrinsic.LOG10), - ('x = mod(a, b)', IntrinsicCall.Intrinsic.MOD), - ('x = matmul(a, b)', IntrinsicCall.Intrinsic.MATMUL), - ('x = max(a, b)', IntrinsicCall.Intrinsic.MAX), - ('x = mAx(a, b, c)', IntrinsicCall.Intrinsic.MAX), - ('x = min(a, b)', IntrinsicCall.Intrinsic.MIN), - ('x = min(a, b, c)', IntrinsicCall.Intrinsic.MIN), - ('x = sign(a, b)', IntrinsicCall.Intrinsic.SIGN), - ('x = sqrt(a)', IntrinsicCall.Intrinsic.SQRT), - ('x = aimag(a)', IntrinsicCall.Intrinsic.AIMAG), - ('x = dprod(a, b)', IntrinsicCall.Intrinsic.DPROD), - ('x = reshape(a, b, c)', IntrinsicCall.Intrinsic.RESHAPE), - ('x = sin(-3.0)', IntrinsicCall.Intrinsic.SIN)]) + "code, expected_intrinsic, arg_names", + [('x = exp(a)', IntrinsicCall.Intrinsic.EXP, + ["x"]), + ('x = sin(a)', IntrinsicCall.Intrinsic.SIN, + ["x"]), + ('x = asin(a)', IntrinsicCall.Intrinsic.ASIN, + ["x"]), + ('idx = ceiling(a)', IntrinsicCall.Intrinsic.CEILING, + ["a"]), + ('x = abs(a)', IntrinsicCall.Intrinsic.ABS, + ["a"]), + ('x = cos(a)', IntrinsicCall.Intrinsic.COS, + ["x"]), + ('x = acos(a)', IntrinsicCall.Intrinsic.ACOS, + ["x"]), + ('x = tan(a)', IntrinsicCall.Intrinsic.TAN, + ["x"]), + ('x = atan(a)', IntrinsicCall.Intrinsic.ATAN, + ["x"]), + ('x = real(a)', IntrinsicCall.Intrinsic.REAL, + ["a"]), + ('x = real(a, 8)', IntrinsicCall.Intrinsic.REAL, + ["a", "kind"]), + ('x = int(a)', IntrinsicCall.Intrinsic.INT, + ["a"]), + ('x = int(a, 8)', IntrinsicCall.Intrinsic.INT, + ["a", "kind"]), + ('x = log(a)', IntrinsicCall.Intrinsic.LOG, + ["x"]), + ('x = log10(a)', IntrinsicCall.Intrinsic.LOG10, + ["x"]), + ('x = mod(a, b)', IntrinsicCall.Intrinsic.MOD, + ["a", "p"]), + ('x = matmul(a, b)', IntrinsicCall.Intrinsic.MATMUL, + ["matrix_a", "matrix_b"]), + ('x = max(a, b)', IntrinsicCall.Intrinsic.MAX, + [None, None]), + ('x = mAx(a, b, c)', IntrinsicCall.Intrinsic.MAX, + [None, None, None]), + ('x = min(a, b)', IntrinsicCall.Intrinsic.MIN, + [None, None]), + ('x = min(a, b, c)', IntrinsicCall.Intrinsic.MIN, + [None, None, None]), + ('x = sign(a, b)', IntrinsicCall.Intrinsic.SIGN, + ["a", "b"]), + ('x = sqrt(a)', IntrinsicCall.Intrinsic.SQRT, + ["x"]), + ('x = aimag(a)', IntrinsicCall.Intrinsic.AIMAG, + ["z"]), + ('x = dprod(a, b)', IntrinsicCall.Intrinsic.DPROD, + ["x", "y"]), + ('x = reshape(a, b, c)', IntrinsicCall.Intrinsic.RESHAPE, + ["source", "shape", "pad"]), + ('x = sin(-3.0)', IntrinsicCall.Intrinsic.SIN, + ["x"])]) @pytest.mark.usefixtures("f2008_parser") -def test_handling_intrinsics(code, expected_intrinsic, symbol_table): +def test_handling_intrinsics(code, expected_intrinsic, arg_names, + symbol_table): '''Test that the fparser2 _intrinsic_handler method deals with Intrinsic_Function_Reference nodes that are translated to PSyIR IntrinsicCall nodes. @@ -289,8 +317,8 @@ def test_handling_intrinsics(code, expected_intrinsic, symbol_table): assert assign.rhs.routine.symbol.intrinsic == expected_intrinsic, \ "Fails when parsing '" + code + "'" assert len(assign.rhs.arguments) == len(assign.rhs.argument_names) - for named_arg in assign.rhs.argument_names: - assert named_arg is None + for i, named_arg in enumerate(assign.rhs.argument_names): + assert named_arg == arg_names[i] def test_handling_unsupported_intrinsics(symbol_table): @@ -315,13 +343,13 @@ def test_handling_unsupported_intrinsics(symbol_table): @pytest.mark.parametrize( "code, expected_intrinsic, expected_names", [('x = sin(a)', - IntrinsicCall.Intrinsic.SIN, [None]), - ('x = sin(array=a)', - IntrinsicCall.Intrinsic.SIN, ["array"]), + IntrinsicCall.Intrinsic.SIN, ["x"]), + ('x = sin(x=a)', + IntrinsicCall.Intrinsic.SIN, ["x"]), ('x = dot_product(a, b)', - IntrinsicCall.Intrinsic.DOT_PRODUCT, [None, None]), + IntrinsicCall.Intrinsic.DOT_PRODUCT, ["vector_a", "vector_b"]), ('x = dot_product(a, vector_b=b)', - IntrinsicCall.Intrinsic.DOT_PRODUCT, [None, "vector_b"]), + IntrinsicCall.Intrinsic.DOT_PRODUCT, ["vector_a", "vector_b"]), ('x = dot_product(vector_a=a, vector_b=b)', IntrinsicCall.Intrinsic.DOT_PRODUCT, ["vector_a", "vector_b"]), ('x = max(a, b, c)', diff --git a/src/psyclone/tests/psyir/frontend/fparser2_test.py b/src/psyclone/tests/psyir/frontend/fparser2_test.py index 068f4d0b0c..bbb71658bb 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_test.py @@ -1194,7 +1194,7 @@ def test_process_array_declarations_bound_expressions(): assert l5dtype.shape[0].lower.intrinsic is IntrinsicCall.Intrinsic.NINT assert isinstance(l5dtype.shape[0].upper, IntrinsicCall) assert l5dtype.shape[0].upper.intrinsic is IntrinsicCall.Intrinsic.NINT - assert l5dtype.shape[0].upper.debug_string() == "NINT(MAXVAL(l4))" + assert l5dtype.shape[0].upper.debug_string() == "NINT(a=MAXVAL(array=l4))" @pytest.mark.usefixtures("f2008_parser") @@ -2619,7 +2619,7 @@ def test_intrinsiccall_args(f2008_parser): intrinsic_node = psyir.walk(IntrinsicCall)[0] assert isinstance(intrinsic_node, IntrinsicCall) assert len(intrinsic_node._argument_names) == len(intrinsic_node.arguments) - arg_names = [None, "dim", "mask"] + arg_names = ["array", "dim", "mask"] for idx, child in enumerate(intrinsic_node.arguments): assert intrinsic_node._argument_names[idx] == ( id(child), arg_names[idx]) diff --git a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py index 894ee8120b..5c3aa31397 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py @@ -280,15 +280,15 @@ def test_basic_where(): assert isinstance(loop.ast, Fortran2003.Where_Construct) assert isinstance(loops[0].start_expr, Literal) - assert loops[0].stop_expr.debug_string() == "SIZE(dry, dim=3)" + assert loops[0].stop_expr.debug_string() == "SIZE(array=dry, dim=3)" ifblock = loops[2].loop_body[0] assert isinstance(ifblock, IfBlock) assert "was_where" in ifblock.annotations assert (ifblock.condition.debug_string() == - "dry(LBOUND(dry, dim=1) + widx1 - 1," - "LBOUND(dry, dim=2) + widx2 - 1," - "LBOUND(dry, dim=3) + widx3 - 1)") + "dry(LBOUND(array=dry, dim=1) + widx1 - 1," + "LBOUND(array=dry, dim=2) + widx2 - 1," + "LBOUND(array=dry, dim=3) + widx3 - 1)") @pytest.mark.usefixtures("parser") @@ -315,9 +315,9 @@ def test_where_array_subsections(): assert isinstance(assign, Assignment) assert isinstance(assign.lhs.children[0], BinaryOperation) assert (assign.lhs.children[0].debug_string() == - "LBOUND(z1_st, dim=1) + widx1 - 1") + "LBOUND(array=z1_st, dim=1) + widx1 - 1") assert (assign.lhs.children[2].debug_string() == - "LBOUND(z1_st, dim=3) + widx2 - 1") + "LBOUND(array=z1_st, dim=3) + widx2 - 1") def test_where_mask_starting_value(fortran_reader, fortran_writer): @@ -423,11 +423,11 @@ def test_where_mask_is_slice_lower_limit(fortran_reader, fortran_writer): psyir = fortran_reader.psyir_from_source(code) out = fortran_writer(psyir) # Check that created loops have the correct number of iterations - assert "do widx2 = 1, UBOUND(picefr, dim=2) - jstart + 1, 1" in out - assert "do widx1 = 1, 4 - LBOUND(picefr, dim=1) + 1, 1" in out + assert "do widx2 = 1, UBOUND(array=picefr, dim=2) - jstart + 1, 1" in out + assert "do widx1 = 1, 4 - LBOUND(array=picefr, dim=1) + 1, 1" in out # Check that the indexing into the mask expression uses the lower bounds # specified in the original slice. - assert ("if (picefr(LBOUND(picefr, dim=1) + widx1 - 1," + assert ("if (picefr(LBOUND(array=picefr, dim=1) + widx1 - 1," "jstart + widx2 - 1) > 1.e-10)" in out) assert ("zevap_ice(widx1,widx2,1) = 0.0" in out) assert "if (picefr(4 + widx1 - 1,jstart + widx2 - 1) > 4.e-10)" in out @@ -496,8 +496,9 @@ def test_where_containing_sum_no_dim(fortran_reader, fortran_writer): routine = psyir.walk(Routine)[0] assert isinstance(routine[0], Loop) output = fortran_writer(psyir) - assert ("SUM(a_i_last_couple) / picefr(LBOUND(picefr, dim=1) + widx1 - 1," - "LBOUND(picefr, dim=2) + widx2 - 1)" in output) + print(output) + assert ("SUM(array=a_i_last_couple) / picefr(LBOUND(array=picefr, dim=1) " + "+ widx1 - 1,LBOUND(array=picefr, dim=2) + widx2 - 1)" in output) def test_where_mask_containing_sum_with_dim(fortran_reader): @@ -555,8 +556,8 @@ def test_where_with_scalar_assignment(fortran_reader, fortran_writer): integer :: widx2 integer :: widx1 - do widx2 = 1, SIZE(dry, dim=3), 1 - do widx1 = 1, SIZE(dry, dim=2), 1 + do widx2 = 1, SIZE(array=dry, dim=3), 1 + do widx1 = 1, SIZE(array=dry, dim=2), 1 if (dry(1,widx1,widx2)) then var1 = depth z1_st(widx1,2,widx2) = var1 / ptsu(widx1,widx2,3) @@ -630,9 +631,9 @@ def test_elsewhere(): assert isinstance(ifblock.condition, BinaryOperation) assert ifblock.condition.operator == BinaryOperation.Operator.GT assert (ifblock.condition.debug_string() == - "ptsu(LBOUND(ptsu, dim=1) + widx1 - 1," - "LBOUND(ptsu, dim=2) + widx2 - 1," - "LBOUND(ptsu, dim=3) + widx3 - 1) > 10._wp") + "ptsu(LBOUND(array=ptsu, dim=1) + widx1 - 1," + "LBOUND(array=ptsu, dim=2) + widx2 - 1," + "LBOUND(array=ptsu, dim=3) + widx3 - 1) > 10._wp") # Check that this IF block has an else body which contains another IF assert ifblock.else_body is not None ifblock2 = ifblock.else_body[0] @@ -641,9 +642,9 @@ def test_elsewhere(): assert isinstance(ifblock2.condition, BinaryOperation) assert ifblock2.condition.operator == BinaryOperation.Operator.LT assert (ifblock2.condition.debug_string() == - "ptsu(LBOUND(ptsu, dim=1) + widx1 - 1," - "LBOUND(ptsu, dim=2) + widx2 - 1," - "LBOUND(ptsu, dim=3) + widx3 - 1) < 0.0_wp") + "ptsu(LBOUND(array=ptsu, dim=1) + widx1 - 1," + "LBOUND(array=ptsu, dim=2) + widx2 - 1," + "LBOUND(array=ptsu, dim=3) + widx3 - 1) < 0.0_wp") # Check that this IF block too has an else body assert isinstance(ifblock2.else_body[0], Assignment) # Check that we have three assignments of the correct form and with the @@ -653,9 +654,9 @@ def test_elsewhere(): for assign in assigns: assert isinstance(assign.lhs, ArrayReference) assert (assign.lhs.debug_string() == - "z1_st(LBOUND(z1_st, dim=1) + widx1 - 1," - "LBOUND(z1_st, dim=2) + widx2 - 1," - "LBOUND(z1_st, dim=3) + widx3 - 1)") + "z1_st(LBOUND(array=z1_st, dim=1) + widx1 - 1," + "LBOUND(array=z1_st, dim=2) + widx2 - 1," + "LBOUND(array=z1_st, dim=3) + widx3 - 1)") assert isinstance(assign.parent.parent, IfBlock) assert isinstance(assigns[0].rhs, BinaryOperation) @@ -808,7 +809,8 @@ def test_where_derived_type(fortran_reader, code, size_arg): loops = psyir.walk(Loop) assert len(loops) == 2 assert isinstance(loops[1].stop_expr, IntrinsicCall) - assert loops[1].stop_expr.debug_string() == f"SIZE({size_arg}, dim=1)" + assert (loops[1].stop_expr.debug_string() == + f"SIZE(array={size_arg}, dim=1)") assert isinstance(loops[1].loop_body[0], IfBlock) # All Range nodes should have been replaced assert not loops[0].walk(Range) @@ -958,7 +960,7 @@ def test_non_array_reduction_intrinsic(fortran_reader, fortran_writer): do widx1 = 1, 100, 1 if (a(widx1) < b(widx1)) then - c(widx1) = SIN(a(widx1)) + c(widx1) = SIN(x=a(widx1)) end if enddo @@ -1037,7 +1039,7 @@ def test_elemental_intrinsic_to_loop(fortran_reader, fortran_writer): integer :: widx1 do widx1 = 1, 100, 1 - if (ABS(a(widx1)) < 2) then + if (ABS(a=a(widx1)) < 2) then b(widx1) = a(widx1) end if enddo @@ -1052,7 +1054,7 @@ def test_elemental_intrinsic_to_loop(fortran_reader, fortran_writer): implicit none real, dimension(100) :: a, b - where(dot_product(a,a(:)) + abs(a) < 2) + where(dot_product(a,a(:)) + abs(a=a) < 2) b = a end where end program @@ -1061,7 +1063,7 @@ def test_elemental_intrinsic_to_loop(fortran_reader, fortran_writer): out = fortran_writer(psyir) assert isinstance(psyir.children[0].children[0], Loop) correct = '''do widx1 = 1, 100, 1 - if (DOT_PRODUCT(a, a(:)) + ABS(a(widx1)) < 2) then + if (DOT_PRODUCT(vector_a=a, vector_b=a(:)) + ABS(a=a(widx1)) < 2) then b(widx1) = a(widx1) end if enddo''' @@ -1125,7 +1127,7 @@ def test_elemental_function_to_loop(fortran_reader, fortran_writer): psyir = fortran_reader.psyir_from_source(code) assert isinstance(psyir.children[0].children[1].children[0], Loop) correct = '''do widx1 = 1, 100, 1 - if (x(a(:)) + ABS(a(widx1)) < 2) then + if (x(a(:)) + ABS(a=a(widx1)) < 2) then b(widx1) = a(widx1) end if enddo''' diff --git a/src/psyclone/tests/psyir/nodes/array_mixin_test.py b/src/psyclone/tests/psyir/nodes/array_mixin_test.py index 4d6c9b7565..349609c68c 100644 --- a/src/psyclone/tests/psyir/nodes/array_mixin_test.py +++ b/src/psyclone/tests/psyir/nodes/array_mixin_test.py @@ -526,7 +526,7 @@ def test_member_get_bound_expression(fortran_writer): lbnd = ref.member._get_bound_expression(0, "lower") assert isinstance(lbnd, IntrinsicCall) out = fortran_writer(lbnd).lower() - assert "lbound(grid_var%data, dim=1)" in out + assert "lbound(array=grid_var%data, dim=1)" in out usym = DataSymbol("uvar", UnresolvedType()) ref = ArrayOfStructuresReference.create( usym, [_ONE.copy()], @@ -535,12 +535,12 @@ def test_member_get_bound_expression(fortran_writer): lbnd = ref.member.member._get_bound_expression(0, "lower") assert isinstance(lbnd, IntrinsicCall) out = fortran_writer(lbnd).lower() - assert "lbound(uvar(1)%map(1,2)%data, dim=1)" in out + assert "lbound(array=uvar(1)%map(1,2)%data, dim=1)" in out ubnd = ref.member._get_bound_expression(0, "upper") assert isinstance(ubnd, IntrinsicCall) out = fortran_writer(ubnd).lower() - assert "ubound(uvar(1)%map, dim=1)" in out + assert "ubound(array=uvar(1)%map, dim=1)" in out # Second, test when we do have type information. a2d = ArrayType(REAL_TYPE, [2, (2, 8)]) # Structure that contains "map" which is a 2D array. @@ -659,19 +659,19 @@ def test_get_effective_shape(fortran_reader): child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert "SIZE(a, dim=1)" in shape[0].debug_string() + assert "SIZE(array=a, dim=1)" in shape[0].debug_string() # Array slice with only lower-bound specified. # a(2:) = 0.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert shape[0].debug_string() == "UBOUND(a, dim=1) - 2 + 1" + assert shape[0].debug_string() == "UBOUND(array=a, dim=1) - 2 + 1" # Array slice with only upper-bound specified. # a(:5) = 0.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert shape[0].debug_string() == "5 - LBOUND(a, dim=1) + 1" + assert shape[0].debug_string() == "5 - LBOUND(array=a, dim=1) + 1" # Array slice with only step specified. # a(::4) = 0.0 child_idx += 1 @@ -680,14 +680,15 @@ def test_get_effective_shape(fortran_reader): # Since step is not unity, this is not a 'full-range' access so we still # end up with UBOUND and LBOUND, even though SIZE would be nicer. assert (shape[0].debug_string() == - "(UBOUND(a, dim=1) - LBOUND(a, dim=1)) / 4 + 1") + "(UBOUND(array=a, dim=1) - LBOUND(array=a, dim=1)) / 4 + 1") # Array slice defined using LBOUND and UBOUND intrinsics but for a # different array altogether. # a(lbound(b,1):ubound(b,2)) = 0.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert shape[0].debug_string() == "UBOUND(b, 2) - LBOUND(b, 1) + 1" + assert (shape[0].debug_string() == + "UBOUND(array=b, dim=2) - LBOUND(array=b, dim=1) + 1") # Indirect array slice. # b(indices(2:3,1), 2:5) = 2.0 child_idx += 1 @@ -724,14 +725,14 @@ def test_get_effective_shape(fortran_reader): shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 assert (shape[0].debug_string().lower() == - "ubound(b, dim=2) - (1 + indices(1,1)) + 1") + "ubound(array=b, dim=2) - (1 + indices(1,1)) + 1") # Array access with indices given by another array that is not explicitly # indexed. # b(idx, a) = -1.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert "SIZE(a)" in shape[0].debug_string() + assert "SIZE(array=a)" in shape[0].debug_string() # Array-index expressions are symbols of unknown type so we don't know # whether we have an array slice or just a scalar. # b(scalarval, arrayval) = 1 @@ -763,8 +764,8 @@ def test_struct_get_effective_shape(fortran_reader): child_idx = 0 shape = routine.children[child_idx].lhs.member._get_effective_shape() assert len(shape) == 2 - assert "SIZE(grid%data, dim=1)" in shape[0].debug_string() - assert "SIZE(grid%data, dim=2)" in shape[1].debug_string() + assert "SIZE(array=grid%data, dim=1)" in shape[0].debug_string() + assert "SIZE(array=grid%data, dim=2)" in shape[1].debug_string() # Slice of ArrayOfStructuresMixin child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() diff --git a/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py b/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py index 4a1d6e5e67..55c7ba18dd 100644 --- a/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py +++ b/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py @@ -2817,12 +2817,12 @@ def test_omp_task_directive_nemolite_boundary( if (.NOT.boundary(i,j) + boundary(i,j + 1) <= (-1)) then if (boundary(i,j) < 0) then jiv = j + 1 - va(i,j) = va(i,jiv) + SQRT(g / hv(i,j)) * (sshn_v(i,j) - \ + va(i,j) = va(i,jiv) + SQRT(x=g / hv(i,j)) * (sshn_v(i,j) - \ sshn_v(i,jiv)) else if (boundary(i,j + 1) < 0) then jiv = j - 1 - va(i,j) = va(i,jiv) + SQRT(g / hv(i,j)) * (sshn_v(i,j) - \ + va(i,j) = va(i,jiv) + SQRT(x=g / hv(i,j)) * (sshn_v(i,j) - \ sshn_v(i,jiv)) end if end if @@ -2842,6 +2842,7 @@ def test_omp_task_directive_nemolite_boundary( enddo enddo !$omp end task''' + print(fortran_writer(tree)) assert correct in fortran_writer(tree) assert Compile(tmpdir).string_compiles(fortran_writer(tree)) @@ -3230,7 +3231,7 @@ def test_omp_task_directive_disallowed_intrinsic(fortran_reader): with pytest.raises(GenerationError) as excinfo: tree.lower_to_language_level() assert ("Attempted to lower to OMPTaskDirective node, but the " - "node contains a 'SUM(b)' intrinsic call, which is not " + "node contains a 'SUM(array=b)' intrinsic call, which is not " "supported." in str(excinfo.value)) @@ -3271,8 +3272,8 @@ def test_omp_task_directive_intrinsic_loop_bound(fortran_reader, ptrans.apply(parent.children) correct = '''\ !$omp task private(i,j) shared(a,b) depend(in: b(:,:)) depend(out: a(:,:)) - do i = LBOUND(a, 2), UBOUND(a, 2), 1 - do j = LBOUND(a, 1), UBOUND(a, 1), 1 + do i = LBOUND(array=a, dim=2), UBOUND(array=a, dim=2), 1 + do j = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1 a(i,j) = b(i,j) + 1 enddo enddo @@ -3319,7 +3320,7 @@ def test_omp_task_directive_intrinsic_loop_step(fortran_reader): tdir.lower_to_language_level() assert ("IntrinsicCall not supported in the step variable of a Loop" " in an OMPTaskDirective node. The step expression is " - "'LBOUND(a, 2)'." in str(excinfo.value)) + "'LBOUND(array=a, dim=2)'." in str(excinfo.value)) def test_evaluate_write_reference_failcase(): diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index e259aa2dfb..f1aed40679 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -543,9 +543,10 @@ def test_index_intrinsic(fortran_reader, fortran_writer): psyir = fortran_reader.psyir_from_source(code) assert len(psyir.walk(IntrinsicCall)) == 3 result = fortran_writer(psyir).lower() - assert "ind1 = index(clname, '_', back=.true.) + 1" in result - assert "ind2 = index(clname, '.') - 1" in result - assert "ind2 = index(clname, '.', kind=4) - 1" in result + assert ("ind1 = index(string=clname, substring='_', back=.true.) + 1" in + result) + assert "ind2 = index(string=clname, substring='.') - 1" in result + assert "ind2 = index(string=clname, substring='.', kind=4) - 1" in result def test_verify_intrinsic(fortran_reader, fortran_writer): @@ -574,13 +575,14 @@ def test_verify_intrinsic(fortran_reader, fortran_writer): # Should have 4 VERIFY and 2 KIND assert len(psyir.walk(IntrinsicCall)) == 6 result = fortran_writer(psyir).lower() - assert "if (verify(clname(ind1:ind2), '0123456789') == 0) then" in result - assert ("if (verify(clname(ind1:ind2), '0123456789', back=.true.) " - "== 0) then" in result) - assert ("if (verify(clname(ind1:ind2), '0123456789', kind=kind(1)) " - "== 0) then" in result) - assert ("if (verify(clname(ind1:ind2), '0123456789', kind=kind(1), " + assert ("if (verify(string=clname(ind1:ind2), set='0123456789') == 0) " + "then" in result) + assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " "back=.true.) == 0) then" in result) + assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " + "kind=kind(x=1)) == 0) then" in result) + assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " + "back=.true., kind=kind(x=1)) == 0) then" in result) def test_intrinsic_canonicalisation(fortran_reader): diff --git a/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py b/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py index f04e60c63b..ae65c64fde 100644 --- a/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py +++ b/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py @@ -64,13 +64,13 @@ def test_type_convert_intrinsic_create(intrinsic, intr_str, fortran_writer): assert intr_call.intrinsic is intrinsic check_links(intr_call, [intr_call.routine, lhs, rhs]) result = fortran_writer(intr_call) - assert intr_str + "(tmp1, kind=wp)" in result.lower() + assert intr_str + "(a=tmp1, kind=wp)" in result.lower() # Kind specified with an integer literal rhs = Literal("4", INTEGER_SINGLE_TYPE) intr_call = IntrinsicCall.create(intrinsic, [lhs.detach(), ("kind", rhs)]) check_links(intr_call, [intr_call.routine, lhs, rhs]) result = fortran_writer(intr_call) - assert intr_str + "(tmp1, kind=4)" in result.lower() + assert intr_str + "(a=tmp1, kind=4)" in result.lower() # Kind specified as an arithmetic expression rhs = BinaryOperation.create(BinaryOperation.Operator.ADD, Reference(wp_sym), @@ -78,7 +78,7 @@ def test_type_convert_intrinsic_create(intrinsic, intr_str, fortran_writer): intr_call = IntrinsicCall.create(intrinsic, [lhs.detach(), ("kind", rhs)]) check_links(intr_call, [intr_call.routine, lhs, rhs]) result = fortran_writer(intr_call) - assert intr_str + "(tmp1, kind=wp + 2)" in result.lower() + assert intr_str + "(a=tmp1, kind=wp + 2)" in result.lower() @pytest.mark.xfail(reason="No PSyIR symbol type checking is performed on the " diff --git a/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py b/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py index 1a08a4455b..275e733253 100644 --- a/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py @@ -138,8 +138,8 @@ def test_apply_multi_dim_imported_limits(fortran_reader, fortran_writer): # We cannot test the compilation of the generated code because of # the 'use some_mod'. assert "real, allocatable, dimension(:,:), private :: a\n" in code - assert (" if (.not.allocated(a) .or. ubound(a, dim=1) /= jpi .or. " - "ubound(a, dim=2) /= jpj) then\n" + assert (" if (.not.allocated(a) .or. ubound(array=a, dim=1) /= jpi " + ".or. ubound(array=a, dim=2) /= jpj) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" @@ -167,8 +167,8 @@ def test_apply_arg_limits(fortran_reader, fortran_writer, tmpdir): hoist_trans.apply(routine) code = fortran_writer(psyir).lower() assert "real, allocatable, dimension(:,:), private :: a\n" in code - assert (" if (.not.allocated(a) .or. ubound(a, dim=1) /= nx .or. " - "ubound(a, dim=2) /= ny) then\n" + assert (" if (.not.allocated(a) .or. ubound(array=a, dim=1) /= nx " + ".or. ubound(array=a, dim=2) /= ny) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" @@ -199,16 +199,16 @@ def test_apply_runtime_checks(fortran_reader, fortran_writer, tmpdir): hoist_trans.apply(routine) code = fortran_writer(psyir).lower() assert "real, allocatable, dimension(:,:), private :: a\n" in code - assert (" if (.not.allocated(a) .or. ubound(a, dim=1) /= nx .or. " - "ubound(a, dim=2) /= ny) then\n" + assert (" if (.not.allocated(a) .or. ubound(array=a, dim=1) /= nx " + ".or. ubound(array=a, dim=2) /= ny) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" " allocate(a(1:nx,1:ny))\n" " end if\n" in code) - assert (" if (.not.allocated(b) .or. lbound(b, dim=1) /= nx .or. " - "ubound(b, dim=1) /= ny .or. lbound(b, dim=2) /= nx .or. " - "ubound(b, dim=2) /= ny) then\n" + assert (" if (.not.allocated(b) .or. lbound(array=b, dim=1) /= nx " + ".or. ubound(array=b, dim=1) /= ny .or. lbound(array=b, dim=2) " + "/= nx .or. ubound(array=b, dim=2) /= ny) then\n" " if (allocated(b)) then\n" " deallocate(b)\n" " end if\n" @@ -244,15 +244,15 @@ def test_apply_multi_arrays(fortran_reader, fortran_writer): assert "real, allocatable, dimension(:,:), private :: a" in code assert "integer, allocatable, dimension(:,:), private :: mask" in code assert ( - " if (.not.allocated(mask) .or. ubound(mask, dim=1) /= jpi .or. " - "ubound(mask, dim=2) /= jpj) then\n" + " if (.not.allocated(mask) .or. ubound(array=mask, dim=1) /= jpi " + ".or. ubound(array=mask, dim=2) /= jpj) then\n" " if (allocated(mask)) then\n" " deallocate(mask)\n" " end if\n" " allocate(mask(1:jpi,1:jpj))\n" " end if\n" - " if (.not.allocated(a) .or. ubound(a, dim=1) /= nx .or. " - "ubound(a, dim=2) /= ny) then\n" + " if (.not.allocated(a) .or. ubound(array=a, dim=1) /= nx .or. " + "ubound(array=a, dim=2) /= ny) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" @@ -283,8 +283,8 @@ def test_apply_name_clash(fortran_reader, fortran_writer, tmpdir): code = fortran_writer(psyir).lower() assert (" real, allocatable, dimension(:,:), private :: a\n" " real, allocatable, dimension(:,:), private :: a_2\n" in code) - assert (" if (.not.allocated(a_2) .or. ubound(a_2, dim=1) /= nx .or. " - "ubound(a_2, dim=2) /= ny) then\n" + assert (" if (.not.allocated(a_2) .or. ubound(array=a_2, dim=1) /= nx " + ".or. ubound(array=a_2, dim=2) /= ny) then\n" " if (allocated(a_2)) then\n" " deallocate(a_2)\n" " end if\n" @@ -743,14 +743,14 @@ def test_apply_with_allocatables(fortran_reader, fortran_writer, tmpdir): if (.NOT.ALLOCATED(a)) then ALLOCATE(a(10)) end if - if (.NOT.ALLOCATED(b) .OR. UBOUND(b, dim=1) /= var) then + if (.NOT.ALLOCATED(b) .OR. UBOUND(array=b, dim=1) /= var) then if (ALLOCATED(b)) then DEALLOCATE(b) end if ALLOCATE(b(10:var)) end if - if (.NOT.ALLOCATED(c) .OR. LBOUND(c, dim=1) /= var - 5 .OR. \ -UBOUND(c, dim=1) /= var + 5) then + if (.NOT.ALLOCATED(c) .OR. LBOUND(array=c, dim=1) /= var - 5 .OR. \ +UBOUND(array=c, dim=1) /= var + 5) then if (ALLOCATED(c)) then DEALLOCATE(c) end if diff --git a/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py b/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py index dd0221ac93..4d11a4c4f6 100644 --- a/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py @@ -78,7 +78,7 @@ def test_apply(fortran_reader, fortran_writer): # Start expression is not hoisted because it is a literal expected = """ loop_step = mytype%step - loop_stop = UBOUND(a, 1) + loop_stop = UBOUND(array=a, dim=1) do i = 1, loop_stop, loop_step a(i) = 1 enddo\n""" @@ -112,10 +112,10 @@ def test_apply_nested(fortran_reader, fortran_writer): trans.apply(loop) # Start expression is not hoisted because it is a simple scalar reference expected = """ - loop_stop = UBOUND(a, 2) + loop_stop = UBOUND(array=a, dim=2) do i = start, loop_stop, 1 - loop_stop_1 = UBOUND(a, 1) - loop_start = LBOUND(a, 1) + loop_stop_1 = UBOUND(array=a, dim=1) + loop_start = LBOUND(array=a, dim=1) do j = loop_start, loop_stop_1, 1 a(j,i) = 1 enddo diff --git a/src/psyclone/tests/psyir/transformations/hoist_trans_test.py b/src/psyclone/tests/psyir/transformations/hoist_trans_test.py index b6ef50a313..6a1a6b353f 100644 --- a/src/psyclone/tests/psyir/transformations/hoist_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/hoist_trans_test.py @@ -513,8 +513,8 @@ def test_validate_array_of_struct(fortran_reader): hoist_trans = HoistTrans() with pytest.raises(TransformationError) as err: hoist_trans.validate(loop.loop_body[0]) - assert ("The statement 'ipi = SIZE(ptab(jf)%pt4d, 1)' can't be hoisted as " - "it reads variable 'jf'" in str(err.value)) + assert ("The statement 'ipi = SIZE(array=ptab(jf)%pt4d, dim=1)' can't be " + "hoisted as it reads variable 'jf'" in str(err.value)) def test_str(): diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py index 00223775c1..8e666816f5 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py @@ -251,7 +251,7 @@ def test_validate_increment_with_unsupported_type(fortran_reader): node = psyir.walk(IntrinsicCall)[0] with pytest.raises(TransformationError) as info: trans.apply(node) - assert ("To loopify 'x(1) = x(1) + MAXVAL(a)' we need a temporary " + assert ("To loopify 'x(1) = x(1) + MAXVAL(array=a)' we need a temporary " "variable, but the type of 'x(1)' can not be resolved or is " "unsupported." in str(info.value)) @@ -373,7 +373,7 @@ def test_mask(fortran_reader, fortran_writer, tmpdir): " result = -HUGE(x=result)\n" " do idx = 1, 10, 1\n" " do idx_1 = 1, 10, 1\n" - " if (MOD(array(idx_1,idx), 2.0) == 1) then\n" + " if (MOD(a=array(idx_1,idx), p=2.0) == 1) then\n" " result = MAX(result, array(idx_1,idx))\n" " end if\n" " enddo\n" @@ -477,7 +477,7 @@ def test_references(fortran_reader, fortran_writer, tmpdir): " do idx = 1, 10, 1\n" " do idx_1 = 1, 10, 1\n" " if (tmask(idx_1,idx) == 1.0) then\n" - " zmax(1) = MAX(zmax(1), ABS(sshn(idx_1,idx) + ssh_ref * " + " zmax(1) = MAX(zmax(1), ABS(a=sshn(idx_1,idx) + ssh_ref * " "tmask(idx_1,idx)))\n" " end if\n" " enddo\n" @@ -506,7 +506,7 @@ def test_nemo_example(fortran_reader, fortran_writer, tmpdir): " do idx = LBOUND(array=sshn, dim=2), UBOUND(array=sshn, dim=2), 1\n" " do idx_1 = LBOUND(array=sshn, dim=1), " "UBOUND(array=sshn, dim=1), 1\n" - " zmax(1) = MAX(zmax(1), ABS(sshn(idx_1,idx) + ssh_ref * " + " zmax(1) = MAX(zmax(1), ABS(a=sshn(idx_1,idx) + ssh_ref * " "tmask(idx_1,idx,1)))\n" " enddo\n" " enddo\n") @@ -634,7 +634,7 @@ def test_multi_intrinsics(fortran_reader, fortran_writer, tmpdir): " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" " x = MAX(x, a(idx))\n" " enddo\n" - " x = x + MAXVAL(b(:))\n") + " x = x + MAXVAL(array=b(:))\n") psyir = fortran_reader.psyir_from_source(code) trans = Maxval2LoopTrans() # FileContainer/Routine/Assignment/BinaryOp/IntrinsicCall diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py index 6acfe13e61..0ad9ca3536 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py @@ -146,8 +146,8 @@ def test_validate_references_matmul(fortran_reader): expected = ( "The DotProduct2CodeTrans transformation only supports the " "transformation of a dotproduct intrinsic if its arguments " - "are plain arrays, but found MATMUL(a3, v1) in " - "DOT_PRODUCT(MATMUL(a3, v1), v2)") + "are plain arrays, but found MATMUL(matrix_a=a3, matrix_b=v1) in " + "DOT_PRODUCT(vector_a=MATMUL(matrix_a=a3, matrix_b=v1), vector_b=v2)") check_validate(code, expected, fortran_reader) @@ -171,8 +171,8 @@ def test_validate_references_structure(fortran_reader): expected = ( "The DotProduct2CodeTrans transformation only supports the " "transformation of a dotproduct intrinsic if its arguments are plain " - "arrays, but found grid%var1(:) in DOT_PRODUCT(grid%var1(:), " - "grid%var2(:)).") + "arrays, but found grid%var1(:) in DOT_PRODUCT(vector_a=grid%var1(:)" + ", vector_b=grid%var2(:)).") check_validate(code, expected, fortran_reader) @@ -192,7 +192,8 @@ def test_validate_1d_array(fortran_reader): "The DotProduct2CodeTrans transformation only supports the " "transformation of a dotproduct intrinsic with an argument not " "containing an array slice if the argument is a 1D array, but " - "found a1 with 2 dimensions in DOT_PRODUCT(a1, a2).") + "found a1 with 2 dimensions in DOT_PRODUCT(vector_a=a1, " + "vector_b=a2).") check_validate(code, expected, fortran_reader) @@ -214,7 +215,7 @@ def test_validate_array_slice_dim1(fortran_reader): "transformation of a dotproduct intrinsic with an argument " "containing an array slice if the array slice is for the 1st " "dimension of the array, but found a2(1,:) in " - "DOT_PRODUCT(a1(:,1), a2(1,:)).") + "DOT_PRODUCT(vector_a=a1(:,1), vector_b=a2(1,:)).") check_validate(code, expected, fortran_reader) @@ -236,7 +237,7 @@ def test_validate_array_full_slice(fortran_reader): "transformation of a dotproduct intrinsic with an argument containing " "an array slice if the argument is for the 1st dimension of the array " "and is for the full range of that dimension, but found a1(2:4,1) in " - "DOT_PRODUCT(a1(2:4,1), a2(:,10)).") + "DOT_PRODUCT(vector_a=a1(2:4,1), vector_b=a2(:,10)).") check_validate(code, expected, fortran_reader) @@ -254,7 +255,8 @@ def test_validate_real(fortran_reader): "end subroutine\n") expected = ( "The DotProduct2CodeTrans transformation only supports arrays of " - "real data, but found v1 of type INTEGER in DOT_PRODUCT(v1, v2).") + "real data, but found v1 of type INTEGER in DOT_PRODUCT(vector_a=v1, " + "vector_b=v2).") check_validate(code, expected, fortran_reader) diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py index 5dded973fc..57f4eff6a1 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py @@ -308,8 +308,8 @@ def test_sign_of_unknown_type(fortran_reader): if call.intrinsic.name == "SIGN"] with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[0]) - assert ("Sign2CodeTrans cannot be applied to 'SIGN(MAX(ABS(ztmp1), " - "1.e-6_wp), ztmp1) because the type of the argument" + assert ("Sign2CodeTrans cannot be applied to 'SIGN(a=MAX(ABS(a=ztmp1), " + "1.e-6_wp), b=ztmp1) because the type of the argument" in str(err.value)) with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[1]) diff --git a/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py b/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py index 82bed49f3e..9dd290e0a7 100644 --- a/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py +++ b/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py @@ -132,8 +132,8 @@ def test_omptask_apply(fortran_reader, fortran_writer): do jj = 1, 10, 1 !$omp task private(ji) firstprivate(jj) shared(t,s) \ depend(in: s(:,jj)) depend(out: t(:,jj)) - do ji = 1, SIZE(ji, 2), 1 - t(ji,jj) = INT(s(ji,jj)) + do ji = 1, SIZE(array=ji, dim=2), 1 + t(ji,jj) = INT(a=s(ji,jj)) enddo !$omp end task enddo From 8f3250fdc876ecb2e5f4e2149fc06a130816151b Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 23 Sep 2025 17:27:26 +0100 Subject: [PATCH 07/50] linting --- .../tests/psyir/frontend/fparser2_intrinsic_handler_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index 786a07d783..dfd9e6cec0 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -295,7 +295,7 @@ def test_intrinsic_handler_intrinsiccall_onearg( ["x", "y"]), ('x = reshape(a, b, c)', IntrinsicCall.Intrinsic.RESHAPE, ["source", "shape", "pad"]), - ('x = sin(-3.0)', IntrinsicCall.Intrinsic.SIN, + ('x = sin(-3.0)', IntrinsicCall.Intrinsic.SIN, ["x"])]) @pytest.mark.usefixtures("f2008_parser") def test_handling_intrinsics(code, expected_intrinsic, arg_names, From 4533878744b7bd4444e3549cf8a21de3c7be93cc Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 25 Sep 2025 14:47:34 +0100 Subject: [PATCH 08/50] Fixed remaining issues --- src/psyclone/psyir/backend/sympy_writer.py | 26 +++++++++++++--------- src/psyclone/psyir/frontend/fparser2.py | 16 ++++--------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index f47ce26b4c..3cbf83e128 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -46,6 +46,7 @@ from psyclone.core import (Signature, AccessSequence, VariablesAccessMap) +from psyclone.errors import GenerationError from psyclone.psyir.backend.fortran import FortranWriter from psyclone.psyir.backend.visitor import VisitorError from psyclone.psyir.frontend.sympy_reader import SymPyReader @@ -757,20 +758,25 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: :returns: the SymPy representation for the Intrinsic. ''' + # Force canoniclisation of the intrinsic + try: + node.canonicalise() + except (GenerationError, NotImplementedError): + raise VisitorError( + f"Sympy handler can't handle an IntrinsicCall that " + f"can't be canonicalised. Use explicit argument names " + f"to force canonicalisation. Failing node was " + f"'{node.debug_string()}'.") + # Sympy does not support argument names, remove them for now if any(node.argument_names): - # TODO #2302: This is not totally right without canonical intrinsic - # positions for arguments. One alternative is to refuse it with: - # raise VisitorError( - # f"Named arguments are not supported by SymPy but found: " - # f"'{node.debug_string()}'.") - # but this leaves sympy comparisons almost always giving false when - # out of order arguments are rare, so instead we ignore it for now. - # It makes a copy (of the parent because if matters to the call # visitor) because we don't want to delete the original arg names - parent = node.parent.copy() - node = parent.children[node.position] + if node.parent: + parent = node.parent.copy() + node = parent.children[node.position] + else: + node = node.copy() for idx in range(len(node.argument_names)): # pylint: disable=protected-access node._argument_names[idx] = (node._argument_names[idx][0], diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index e13ce745dc..0f59ed6f18 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -4971,18 +4971,18 @@ def _intrinsic_handler(self, node, parent): if not intrinsic.optional_args: # Intrinsics with no optional arguments call = IntrinsicCall(intrinsic, parent=parent) - call = self._process_args(node, call) + call = self._process_args(node, call, True) call.canonicalise() return call if intrinsic.name.lower() in ["minval", "maxval", "sum"]: # Intrinsics with optional arguments require a # canonicalise function call = IntrinsicCall(intrinsic, parent=parent) - call = self._process_args(node, call) + call = self._process_args(node, call, True) call.canonicalise() return call call = IntrinsicCall(intrinsic, parent=parent) - call = self._process_args(node, call) + call = self._process_args(node, call, True) call.canonicalise() return call except KeyError as err: @@ -5287,7 +5287,7 @@ def _call_handler(self, node, parent): return self._process_args(node, call) - def _process_args(self, node, call, canonicalise=None): + def _process_args(self, node, call, canonicalise: bool = False): '''Processes fparser2 call or intrinsic arguments contained in the node argument and adds them to the PSyIR Call or IntrinsicCall contained in the call argument, respectively. @@ -5348,14 +5348,6 @@ def _process_args(self, node, call, canonicalise=None): f"In Fortran, all named arguments should follow all " f"positional arguments, but found '{node}'.") - # Call the canonicalise function if it is supplied. This - # re-orders arg_nodes and renames arg_names appropriately for - # the particular call to make a canonical version. This is - # required because intrinsics can be written with and without - # named arguments (or combinations thereof) in Fortran. - if canonicalise: - canonicalise(arg_nodes, arg_names, node) - self.process_nodes(parent=call, nodes=arg_nodes) # Detach the arguments and add them again with the argument From 808ad043fe051a39b2c450ab59adc22bee6673f2 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 26 Sep 2025 10:33:06 +0100 Subject: [PATCH 09/50] Coverage fix --- .../intrinsics/array_reduction_base_trans.py | 11 +++---- .../tests/psyir/backend/sympy_writer_test.py | 30 ++++++++++++++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py index 6908eec182..1734c08144 100644 --- a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py +++ b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py @@ -78,14 +78,11 @@ def _get_args(node): # Determine the arguments to the intrinsic args = [None, None, None] arg_names_map = {"array": 0, "dim": 1, "mask": 2} + # Canonicalise the intrinsic to resolve the names. + node.canonicalise() for idx, child in enumerate(node.arguments): - if not node.argument_names[idx]: - # positional arg - args[idx] = child - else: - # named arg - name = node.argument_names[idx].lower() - args[arg_names_map[name]] = child + name = node.argument_names[idx].lower() + args[arg_names_map[name]] = child return tuple(args) def __str__(self): diff --git a/src/psyclone/tests/psyir/backend/sympy_writer_test.py b/src/psyclone/tests/psyir/backend/sympy_writer_test.py index 9dedf452dd..c166cbb83d 100644 --- a/src/psyclone/tests/psyir/backend/sympy_writer_test.py +++ b/src/psyclone/tests/psyir/backend/sympy_writer_test.py @@ -46,7 +46,9 @@ from psyclone.psyir.frontend.sympy_reader import SymPyReader from psyclone.psyir.backend.sympy_writer import SymPyWriter from psyclone.psyir.backend.visitor import VisitorError -from psyclone.psyir.nodes import Assignment, Literal, Node +from psyclone.psyir.nodes import ( + Assignment, Literal, Node, IntrinsicCall, Reference +) from psyclone.psyir.symbols import (ArrayType, BOOLEAN_TYPE, CHARACTER_TYPE, INTEGER_TYPE) @@ -325,6 +327,32 @@ def test_sympy_writer_type_map(expr, sym_map, fortran_reader): assert writer._sympy_type_map.keys() == sym_map.keys() +def test_sympy_writer_type_map_non_canonical(fortran_reader): + ''' Test we get an error when the intrinsic can't be canonicalised.''' + source = """program test_prog + use my_mod + integer :: i, j, k + end program test_prog""" + psyir = fortran_reader.psyir_from_source(source) + # Create an ambigious intrinsic. + routine = psyir.children[0] + ref_i = Reference(routine.symbol_table.lookup("i")) + ref_j = Reference(routine.symbol_table.lookup("j")) + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(ref_i) + intrinsic.addchild(ref_j) + assign = Assignment.create(ref_i.copy(), intrinsic) + routine.addchild(assign) + + writer = SymPyWriter() + with pytest.raises(VisitorError) as err: + _ = writer([assign.rhs]) + assert ("Sympy handler can't handle an IntrinsicCall that can't be " + "canonicalised. Use explicit argument names to force " + "canonicalisation. Failing node was 'SUM(i, j)'." + in str(err.value)) + + def test_sym_writer_parse_expr(fortran_reader): '''Tests that convenience function `parse_expr` works as expected. From 893de296922edc825c9a247b75dbe371c255cce6 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 26 Sep 2025 10:34:25 +0100 Subject: [PATCH 10/50] Removed unneccessary canonicalise_minmaxsum routine --- src/psyclone/psyir/frontend/fparser2.py | 98 ------------------- .../fparser2_intrinsic_handler_test.py | 71 +------------- 2 files changed, 1 insertion(+), 168 deletions(-) diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index 0f59ed6f18..aa0efde5e3 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -109,104 +109,6 @@ SUPPORTED_ROUTINE_PREFIXES = ["ELEMENTAL", "PURE", "IMPURE"] -# TODO #2302: It may be that this method could be made more general so -# that it works for more intrinsics, to help minimise the number of -# canonicalise_* functions. -def _canonicalise_minmaxsum(arg_nodes, arg_names, node): - '''Canonicalise the arguments to any of the minval, maxval or sum - intrinsics. These three intrinsics can use the same function as - they have the same argument rules: - - RESULT = [MINVAL, MAXVAL, SUM](ARRAY[, MASK]) - RESULT = [MINVAL, MAXVAL, SUM](ARRAY, DIM[, MASK]) - - This function re-orderes and modifies the supplied arguments a - canonical form so that the PSyIR does not need to support the - different forms that are allowed in Fortran. - - In general Fortran supports all arguments being named, all - arguments being positional and everything in-between, as long as - all named arguments follow all positional arguments. - - For example, both SUM(A, DIM, MASK) and SUM(DIM=DIM, MASK=MASK, - ARRAY=A) are equivalent in Fortran. - - The PSyIR canonical form has all required arguments as positional - arguments and all optional arguments as named arguments, which - would result in SUM(A, DIM=DIM, MASK=MASK) in this case. Note that - the canonical form does not constrain the order of named - arguments. - - In the case where the argument type needs to be determined in - order to create the PSyIR canonical form a CodeBlock is used (by - raising NotImplementedError). - - :param arg_nodes: a list of fparser2 arguments. - :type arg_nodes: List[:py:class:`fparser.two.utils.Base`] - :param arg_names: a list of named-argument names. - :type arg_names: List[Union[str, None]] - :param node: the PSyIR Call or IntrinsicCall node. - :type node: :py:class:`psyclone.psyir.nodes.Call` or \ - :py:class:`psyclone.psyir.nodes.IntrinsicCall` - - :raises InternalError: if the array argument is not found in the \ - argument list. - :raises NotImplementedError: if there are two arguments and both \ - of them are not named as the second argument could be a \ - dimension or a mask and it is not currently possible to \ - determine which. - - ''' - # if the array argument is named then make it the first positional - # argument. Simply checking arg_names[0] is OK as, if the first - # argument is named, then all arguments must be named (to be valid - # Fortran). - if arg_names[0]: - arg_name_index = 0 - for name in arg_names: - if name.lower() == "array": - break - arg_name_index += 1 - else: - raise InternalError( - f"Invalid intrinsic arguments found. Expecting one " - f"of the named arguments to be 'array', but found " - f"'{node}'.") - # Remove the argument name and add an empty argument name to - # the start of the list. - _ = arg_names.pop(arg_name_index) - arg_names.insert(0, None) - # Move the array argument to the start of the list. - node = arg_nodes.pop(arg_name_index) - arg_nodes.insert(0, node) - return - - num_arg_names = len([arg_name for arg_name in arg_names - if arg_name]) - - # If there are two arguments and they are both not - # named then the second argument could be a dim - # (integer) or mask (logical) argument. We could - # attempt to determine the datatype of the argument - # but for the moment give up and return a CodeBlock. - if len(arg_nodes) == 2 and num_arg_names == 0: - raise NotImplementedError( - f"In '{node}' there are two arguments that are not named. " - f"The second could be a dim or a mask so we need datatype " - f"information to determine which and we do not determine " - f"this information at the moment.") - - # If there are three arguments, and fewer than two are - # named, then the argument order is known, so we can just - # add any missing named arguments. - if len(arg_nodes) == 3 and num_arg_names < 2: - # Update the existing list otherwise changes are - # local to this function. - arg_names[0] = None - arg_names[1] = "dim" - arg_names[2] = "mask" - - def _first_type_match(nodelist, typekind): '''Returns the first instance of the specified type in the given node list. diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index dfd9e6cec0..e2ec46b7ef 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -48,7 +48,7 @@ from psyclone.errors import InternalError from psyclone.psyir.frontend.fparser2 import ( - Fparser2Reader, _get_arg_names, _canonicalise_minmaxsum) + Fparser2Reader, _get_arg_names) from psyclone.psyir.nodes import ( Schedule, Assignment, Reference, IntrinsicCall, Literal, CodeBlock) from psyclone.psyir.symbols import ( @@ -101,75 +101,6 @@ def _get_intrinsic_info(string): return (arg_nodes, arg_names, intrinsic) -@pytest.mark.usefixtures("f2008_parser") -def test_canonicalise_minmaxsum(): - '''Check that the _canonicalise_minmaxsum function in fparser2.py - works as expected. - - ''' - # All args named a) first arg first, b) first arg not first c) - # array name not found. - for string in [ - "sum(array=a, dim=d, mask=m)", - "sum(dim=d, array=a, mask=m)", - "sum(dim=d, mask=m, array=a)"]: - arg_nodes, arg_names, intrinsic = _get_intrinsic_info(string) - _canonicalise_minmaxsum(arg_nodes, arg_names, intrinsic) - assert arg_names == [None, 'dim', 'mask'] - assert arg_nodes[0].string == "a" - assert arg_nodes[1].string == "d" - assert arg_nodes[2].string == "m" - - string = "sum(arg1=a, arg2=d, arg3=m)" - arg_nodes, arg_names, intrinsic = _get_intrinsic_info(string) - with pytest.raises(InternalError) as info: - _canonicalise_minmaxsum(arg_nodes, arg_names, intrinsic) - assert ("Invalid intrinsic arguments found. Expecting one of the named " - "arguments to be 'array', but found 'SUM(arg1 = a, arg2 = d, " - "arg3 = m)'." in str(info.value)) - - # Two arguments and the second is not named. Returns - # NotImplementedError which results in a CodeBlock as we need to - # try to determine the datatypes to disambiguate and don't do that - # yet. - arg_nodes, arg_names, intrinsic = _get_intrinsic_info("sum(a, d)") - with pytest.raises(NotImplementedError) as info: - _canonicalise_minmaxsum(arg_nodes, arg_names, intrinsic) - assert (str(info.value) == - "In 'SUM(a, d)' there are two arguments that are not named. " - "The second could be a dim or a mask so we need datatype " - "information to determine which and we do not determine " - "this information at the moment.") - - # Optional arguments are not named but can be determined from - # their order. Canonical form has them named. The last version - # shows that an argument in canonical form remains unchanged. Any - # upper case named-argument names become lower case. - for string in [ - "sum(a, d, m)", - "sum(a, d, mask=m)", - "sum(a, d, MASK=m)"]: - arg_nodes, arg_names, intrinsic = _get_intrinsic_info(string) - _canonicalise_minmaxsum(arg_nodes, arg_names, intrinsic) - assert arg_names == [None, 'dim', 'mask'] - assert arg_nodes[0].string == "a" - assert arg_nodes[1].string == "d" - assert arg_nodes[2].string == "m" - - # Already in canonical form so no change from input to output. - for string in [ - "SUM(a)", - "SUM(a, dim = d)", - "SUM(a, dim = d, mask = m)", - "SUM(a, mask = m)", - "SUM(a, mask = m, dim = d)"]: - arg_nodes, arg_names, intrinsic = _get_intrinsic_info(string) - _canonicalise_minmaxsum(arg_nodes, arg_names, intrinsic) - intrinsic._children = arg_nodes - intrinsic.arg_names = arg_names - assert str(intrinsic) == string - - @pytest.mark.parametrize("arguments", ["a, dim=d, mask=m", "a, d, m"]) @pytest.mark.parametrize("intrinsic_name", ["MINVAL", "MAXVAL", "SUM"]) def test_intrinsic_handler_intrinsiccall_mms( From c856f7c1d8e51816436218f7604767a871e0b21c Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 26 Sep 2025 10:34:53 +0100 Subject: [PATCH 11/50] linting --- .../tests/psyir/frontend/fparser2_intrinsic_handler_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index e2ec46b7ef..58e85e3712 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -46,7 +46,6 @@ from fparser.two import Fortran2003 from fparser.two.Fortran2003 import Execution_Part -from psyclone.errors import InternalError from psyclone.psyir.frontend.fparser2 import ( Fparser2Reader, _get_arg_names) from psyclone.psyir.nodes import ( From a36b0374e5c82cbfe0f4fbd1605db79b5c68be31 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 26 Sep 2025 14:09:40 +0100 Subject: [PATCH 12/50] Removed unneeded function from fparser tests --- .../fparser2_intrinsic_handler_test.py | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index 58e85e3712..a99ed55eaf 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -78,28 +78,6 @@ def make_symbol_table(): return symbol_table -def _get_intrinsic_info(string): - '''Utility function to take a Fortran string for a Fortran intrinsic - call and return its fparser2 tree as well as its arguments as a - list and its the names of its named arguments as a list. - - :param str string: Fortran instrinsic call to be processed. - - :returns: the fparser2 argument nodes as a list, the names of any \ - named arguments as a list and the fparser2 tree of the \ - intrinsic call. - :rtype: Tuple[List[:py:class:`fparser.two.utils.Base`], \ - List[Union[str, None]], \ - :py:class:`fparser.two.Fortran2003.Intrinsic_Function_Reference`) - - ''' - reader = FortranStringReader(string) - intrinsic = Fortran2003.Intrinsic_Function_Reference(reader) - args = intrinsic.items[1].items - arg_nodes, arg_names = _get_arg_names(args) - return (arg_nodes, arg_names, intrinsic) - - @pytest.mark.parametrize("arguments", ["a, dim=d, mask=m", "a, d, m"]) @pytest.mark.parametrize("intrinsic_name", ["MINVAL", "MAXVAL", "SUM"]) def test_intrinsic_handler_intrinsiccall_mms( From b6879e0218db758ab6abb75d76e3ff0149aad36d Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 26 Sep 2025 14:10:08 +0100 Subject: [PATCH 13/50] linting --- .../tests/psyir/frontend/fparser2_intrinsic_handler_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index a99ed55eaf..478450ce61 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -43,11 +43,10 @@ import pytest from fparser.common.readfortran import FortranStringReader -from fparser.two import Fortran2003 from fparser.two.Fortran2003 import Execution_Part from psyclone.psyir.frontend.fparser2 import ( - Fparser2Reader, _get_arg_names) + Fparser2Reader) from psyclone.psyir.nodes import ( Schedule, Assignment, Reference, IntrinsicCall, Literal, CodeBlock) from psyclone.psyir.symbols import ( From a0d5fa6466994e8b6928c42125b860c220a22a74 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 2 Oct 2025 15:58:54 +0100 Subject: [PATCH 14/50] Fixed new test with intrinsic from master --- src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py index f7564301fe..cb73c742e5 100644 --- a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py @@ -84,7 +84,7 @@ def test_gen_param_decls_dependencies(fortran_writer): assert (result == "integer, parameter :: rlg = 8\n" "integer, parameter :: wp = rlg\n" "integer, parameter :: var = rlg + wp\n" - "integer, parameter :: circle = HUGE(circle)\n") + "integer, parameter :: circle = HUGE(x=circle)\n") # Check that an (invalid, obviously) circular dependency is handled. # Replace "rlg" with a new one that depends on "wp". From fa98da645f4e030fbf97b646e3a3efe0f87a883b Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 3 Oct 2025 14:22:50 +0100 Subject: [PATCH 15/50] Changes for first review --- src/psyclone/psyir/backend/sympy_writer.py | 6 +- src/psyclone/psyir/backend/visitor.py | 1 + src/psyclone/psyir/frontend/fparser2.py | 41 +- src/psyclone/psyir/nodes/intrinsic_call.py | 1268 +++++++++++++---- src/psyclone/tests/psyad/tl2ad_test.py | 2 - .../tests/psyir/nodes/intrinsic_call_test.py | 8 +- 6 files changed, 1050 insertions(+), 276 deletions(-) diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index 0e6f2914cf..958e8e7bb7 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -758,15 +758,15 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: :returns: the SymPy representation for the Intrinsic. ''' - # Force canoniclisation of the intrinsic + # Force canonicalisation of the intrinsic try: node.canonicalise() - except (GenerationError, NotImplementedError): + except (GenerationError, NotImplementedError) as err: raise VisitorError( f"Sympy handler can't handle an IntrinsicCall that " f"can't be canonicalised. Use explicit argument names " f"to force canonicalisation. Failing node was " - f"'{node.debug_string()}'.") + f"'{node.debug_string()}'.") from err # Sympy does not support argument names, remove them for now if any(node.argument_names): diff --git a/src/psyclone/psyir/backend/visitor.py b/src/psyclone/psyir/backend/visitor.py index cf8ef11a81..a577b9bd04 100644 --- a/src/psyclone/psyir/backend/visitor.py +++ b/src/psyclone/psyir/backend/visitor.py @@ -269,6 +269,7 @@ def _visit(self, node): possible_method_names = [curr_class.__name__.lower()+"_node" for curr_class in inspect.getmro(type(node))] possible_method_names.remove("object_node") + # Try to call methods using the class names in the order of # the class hierarchy (starting from the current class name). for method_name in possible_method_names: diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index ed5fb7e32e..5dac15cad8 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -44,7 +44,7 @@ from dataclasses import dataclass, field import os import sys -from typing import Iterable, Optional +from typing import Iterable, Optional, Union from fparser.common.readfortran import FortranStringReader from fparser.two import C99Preprocessor, Fortran2003, utils @@ -4897,19 +4897,6 @@ def _intrinsic_handler(self, node, parent): try: intrinsic = IntrinsicCall.Intrinsic[node.items[0].string.upper()] - if not intrinsic.optional_args: - # Intrinsics with no optional arguments - call = IntrinsicCall(intrinsic, parent=parent) - call = self._process_args(node, call, True) - call.canonicalise() - return call - if intrinsic.name.lower() in ["minval", "maxval", "sum"]: - # Intrinsics with optional arguments require a - # canonicalise function - call = IntrinsicCall(intrinsic, parent=parent) - call = self._process_args(node, call, True) - call.canonicalise() - return call call = IntrinsicCall(intrinsic, parent=parent) call = self._process_args(node, call, True) call.canonicalise() @@ -5216,7 +5203,13 @@ def _call_handler(self, node, parent): return self._process_args(node, call) - def _process_args(self, node, call, canonicalise: bool = False): + def _process_args(self, node: Union[ + Fortran2003.Call_Stmt, + Fortran2003.Intrinsic_Function_Reference + ], + call: Union[Call, IntrinsicCall], + canonicalise: bool = False) -> Union[ + Call, IntrinsicCall]: '''Processes fparser2 call or intrinsic arguments contained in the node argument and adds them to the PSyIR Call or IntrinsicCall contained in the call argument, respectively. @@ -5233,23 +5226,17 @@ def _process_args(self, node, call, canonicalise: bool = False): all optional arguments as named arguments, which would result in sum(a, dim=dim, mask=mask) in this case. - :param node: an fparser call node representing a call or \ + :param node: an fparser call node representing a call or an intrinsic call. - :type node: :py:class:`fparser.two.Fortran2003.Call_Stmt` or \ - :py:class:`fparser.two.Fortran2003.Intrinsic_Function_Reference` - :param call: a PSyIR call argument representing a call or an \ + :param call: a PSyIR call argument representing a call or an intrinsic call. - :type call: :py:class:`psyclone.psyir.nodes.Call` or \ - :py:class:`psyclone.psyir.nodes.IntrinsicCall` - :param function canonicalise: a function that canonicalises \ - the call arguments. + :param canonicalise: whether to canonicalise the call (for + IntrinsicCalls). - :returns: the PSyIR call argument with the PSyIR \ + :returns: the PSyIR call argument with the PSyIR representation of the fparser2 node arguments. - :rtype: :py:class:`psyclone.psyir.nodes.Call` or \ - :py:class:`psyclone.psyir.nodes.IntrinsicCall` - :raises GenerationError: if all named arguments do not follow \ + :raises GenerationError: if all named arguments do not follow all positional arguments. ''' diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 4719b22118..9517d499e9 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -68,7 +68,9 @@ # a particular intrinsic. If there's no limit on the number of arguments # then `max_count` will be None. If max_count is not None, then arg_names # will contain a list of the argument names of the required arguments, in -# the order defined by the standard. +# the order defined by the standard. If max_count is None, arg_names will +# be a tuple containing None to ensure the canonicalisation logic still +# works. ArgDesc = namedtuple('ArgDesc', 'min_count max_count types arg_names') @@ -114,7 +116,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, None, Reference, ((None,),)), + required_args=ArgDesc( + min_count=1, + max_count=None, + types=Reference, + arg_names=((None,),)), optional_args={ "mold": Reference, "source": Reference, @@ -129,7 +135,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, None, Reference, ((None,),)), + required_args=ArgDesc( + min_count=1, + max_count=None, + types=Reference, + arg_names=((None,),)), optional_args={"stat": Reference}, return_type=None, reference_accesses=None, @@ -139,7 +149,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, None, Reference, ((None,),)), + required_args=ArgDesc( + min_count=1, + max_count=None, + types=Reference, + arg_names=((None,),)), optional_args={}, return_type=None, reference_accesses=None, @@ -151,7 +165,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -161,7 +179,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -171,7 +193,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -181,7 +207,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -191,7 +221,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("string",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("string",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -201,7 +235,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("string",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("string",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -211,7 +249,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("z",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("z",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -221,7 +263,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -231,7 +277,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("mask",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("mask",),)), optional_args={"dim": DataNode}, return_type=None, reference_accesses=None, @@ -242,7 +292,11 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=True, # Argname of allocated depends on the input. - required_args=ArgDesc(1, 1, DataNode, (("",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -252,7 +306,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -262,7 +320,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("mask",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("mask",),)), optional_args={"dim": DataNode}, return_type=None, reference_accesses=None, @@ -272,7 +334,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -282,7 +348,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -292,7 +362,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("pointer",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("pointer",),)), optional_args={"target": DataNode}, return_type=None, reference_accesses=None, @@ -302,7 +376,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 2, DataNode, (("x",), ("y", "x"))), + required_args=ArgDesc( + min_count=1, + max_count=2, + types=DataNode, + arg_names=(("x",), ("y", "x"))), optional_args={}, # N. B. If this has 2 arguments then the return value # is the of the second argument, however the standard defines @@ -315,7 +393,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("y", "x"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("y", "x"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -325,7 +407,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -335,7 +421,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -345,7 +435,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -356,7 +450,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 4, 4, DataNode, ( + min_count=4, + max_count=4, + types=DataNode, + arg_names=( ("atom", "old", "compare", "new"), ) ), @@ -369,7 +466,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -380,7 +481,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 3, 3, DataNode, ( + min_count=3, + max_count=3, + types=DataNode, + arg_names=( ("atom", "value", "old"), ) ), @@ -394,7 +498,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 3, 3, DataNode, ( + min_count=3, + max_count=3, + types=DataNode, + arg_names=( ("atom", "value", "old"), ) ), @@ -408,7 +515,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 3, 3, DataNode, ( + min_count=3, + max_count=3, + types=DataNode, + arg_names=( ("atom", "value", "old"), ) ), @@ -422,7 +532,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 3, 3, DataNode, ( + min_count=3, + max_count=3, + types=DataNode, + arg_names=( ("atom", "value", "old"), ) ), @@ -435,7 +548,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -445,7 +562,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("value", "atom"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("value", "atom"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -455,7 +576,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("atom", "value"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("atom", "value"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -465,7 +590,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -475,7 +604,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -488,7 +621,10 @@ class Intrinsic(IAttr, Enum): is_elemental=None, is_inquiry=False, required_args=ArgDesc( - 2, 3, DataNode, ( + min_count=2, + max_count=3, + types=DataNode, + arg_names=( ("n", "x"), ("n1", "n2", "x"), ) @@ -502,7 +638,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -512,7 +652,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -525,7 +669,10 @@ class Intrinsic(IAttr, Enum): is_elemental=None, is_inquiry=False, required_args=ArgDesc( - 2, 3, DataNode, ( + min_count=2, + max_count=3, + types=DataNode, + arg_names=( ("n", "x"), ("n1", "n2", "x"), ) @@ -539,7 +686,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -549,7 +700,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -559,7 +714,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -569,7 +728,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -579,7 +742,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -589,7 +756,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "pos"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "pos"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -599,7 +770,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -609,7 +784,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -619,7 +798,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={"Y": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -630,7 +813,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, ( + min_count=2, + max_count=2, + types=DataNode, + arg_names=( ("a", "source_image"), ) ), @@ -643,7 +829,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -655,7 +845,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -668,7 +862,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, ( + min_count=2, + max_count=2, + types=DataNode, + arg_names=( ("a", "operation"), ) ), @@ -683,7 +880,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"result_image": DataNode, "stat": DataNode, "errmsg": DataNode}, @@ -695,7 +896,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, None, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=None, + arg_names=()), optional_args={}, return_type=None, reference_accesses=None, @@ -705,7 +910,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("z",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("z",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -715,7 +924,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -725,7 +938,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -735,7 +952,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("coarray",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("coarray",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -745,7 +966,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("mask",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("mask",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -755,7 +980,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("time",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("time",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -765,7 +994,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("array", "shift"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("array", "shift"),)), optional_args={"dim": DataNode}, return_type=None, reference_accesses=None, @@ -775,7 +1008,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={ "date": DataNode, "time": DataNode, @@ -790,7 +1027,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -800,7 +1041,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -810,7 +1055,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("x", "y"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("x", "y"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -821,7 +1070,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, (("vector_a", "vector_b"),) + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("vector_a", "vector_b"),) ), optional_args={}, return_type=None, @@ -832,7 +1084,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("x", "y"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("x", "y"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -842,7 +1098,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode, (("i", "j", "shift"),)), + required_args=ArgDesc( + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("i", "j", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -852,7 +1112,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode, (("i", "j", "shift"),)), + required_args=ArgDesc( + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("i", "j", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -862,7 +1126,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("array", "shift"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("array", "shift"),)), optional_args={"boundary": DataNode, "dim": DataNode}, return_type=None, reference_accesses=None, @@ -872,7 +1140,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -882,7 +1154,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -892,7 +1168,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -902,7 +1182,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -912,7 +1196,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("event", "count"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("event", "count"),)), optional_args={"stat": DataNode}, return_type=None, reference_accesses=None, @@ -922,7 +1210,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("command",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("command",),)), optional_args={ "wait": DataNode, "exitstat": DataNode, @@ -937,7 +1229,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -947,7 +1243,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -957,7 +1257,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(2, 2, DataNode, (("a", "mold"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("a", "mold"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -967,7 +1271,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={"team": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -978,7 +1286,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 2, 3, DataNode, ( + min_count=2, + max_count=3, + types=DataNode, + arg_names=( ("array", "value", "dim"), ("array", "value") ) @@ -994,7 +1305,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1004,7 +1319,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1014,7 +1333,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1024,7 +1347,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1034,7 +1361,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={ "command": DataNode, "length": DataNode, @@ -1049,7 +1380,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("number",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("number",),)), optional_args={ "value": DataNode, "length": DataNode, @@ -1064,7 +1399,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("name",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("name",),)), optional_args={ "value": DataNode, "length": DataNode, @@ -1080,7 +1419,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={"level": DataNode}, return_type=None, reference_accesses=None, @@ -1090,7 +1433,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, (Reference, Literal), (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=(Reference, Literal), + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1100,7 +1447,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("x", "y"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("x", "y"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1110,7 +1461,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("c",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("c",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1121,7 +1476,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1135,7 +1493,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1146,7 +1508,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1160,7 +1525,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "pos"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "pos"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1170,7 +1539,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode, (("i", "pos", "len"),)), + required_args=ArgDesc( + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("i", "pos", "len"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1180,7 +1553,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "pos"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "pos"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1190,7 +1567,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("c",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("c",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1200,7 +1581,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1212,7 +1597,11 @@ class Intrinsic(IAttr, Enum): is_inquiry=True, # Argument names depend on input, as TEAM vs TEAM_NUMBER # are not distinguishable without context. - required_args=ArgDesc(2, 3, DataNode, (("",),)), + required_args=ArgDesc( + min_count=2, + max_count=3, + types=DataNode, + arg_names=(("",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1222,7 +1611,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("image",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("image",),)), optional_args={"team": DataNode}, return_type=None, reference_accesses=None, @@ -1232,7 +1625,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("string", "substring"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("string", "substring"),)), optional_args={"back": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1242,7 +1639,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1252,7 +1653,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "j"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "j"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1263,7 +1668,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1277,7 +1685,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("array",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("array",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1287,7 +1699,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1297,7 +1713,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1307,7 +1727,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "shift"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1317,7 +1741,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("i", "shift"))), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("i", "shift"))), optional_args={"size": DataNode}, return_type=None, reference_accesses=None, @@ -1327,7 +1755,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1337,7 +1769,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("array",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("array",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1347,7 +1783,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("coarray",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("coarray",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -1357,7 +1797,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1367,7 +1811,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("string",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("string",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1377,7 +1825,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("string",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("string",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1388,8 +1840,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, - (("string_a", "string_b"),) + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("string_a", "string_b"),) ), optional_args={}, return_type=None, @@ -1401,8 +1855,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, - (("string_a", "string_b"),) + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("string_a", "string_b"),) ), optional_args={}, return_type=None, @@ -1414,8 +1870,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, - (("string_a", "string_b"),) + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("string_a", "string_b"),) ), optional_args={}, return_type=None, @@ -1427,8 +1885,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, - (("string_a", "string_b"),) + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("string_a", "string_b"),) ), optional_args={}, return_type=None, @@ -1439,7 +1899,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1449,7 +1913,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1459,7 +1927,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1469,7 +1941,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("l",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("l",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1479,7 +1955,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1489,7 +1969,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1500,7 +1984,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 2, 2, DataNode, (("matrix_a", "matrix_b"),) + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("matrix_a", "matrix_b"),) ), optional_args={}, return_type=None, @@ -1513,7 +2000,11 @@ class Intrinsic(IAttr, Enum): is_inquiry=False, # No upper limit on argument type so we don't store an # argument list of names. - required_args=ArgDesc(2, None, DataNode, ((None,),)), + required_args=ArgDesc( + min_count=2, + max_count=None, + types=DataNode, + arg_names=((None,),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1523,7 +2014,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1534,7 +2029,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1553,7 +2051,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1567,8 +2068,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode, - (("tsource", "fsource", "mask"),)), + required_args=ArgDesc( + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("tsource", "fsource", "mask"),) + ), optional_args={}, return_type=None, reference_accesses=None @@ -1578,7 +2083,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode, (("i", "j", "mask"),)), + required_args=ArgDesc( + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("i", "j", "mask"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1590,7 +2099,11 @@ class Intrinsic(IAttr, Enum): is_inquiry=False, # No upper limit on argument type so we don't store an # argument list of names. - required_args=ArgDesc(2, None, DataNode, ((None,),)), + required_args=ArgDesc( + min_count=2, + max_count=None, + types=DataNode, + arg_names=((None,),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1600,7 +2113,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1611,7 +2128,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1630,7 +2150,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1644,7 +2167,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("a", "p"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("a", "p"),)), optional_args={}, return_type=None, reference_accesses=None @@ -1654,7 +2181,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("a", "p"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("a", "p"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1664,7 +2195,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("from", "to"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("from", "to"),)), optional_args={"stat": DataNode, "errmsg": DataNode}, return_type=None, reference_accesses=None, @@ -1675,7 +2210,10 @@ class Intrinsic(IAttr, Enum): is_elemental=True, is_inquiry=False, required_args=ArgDesc( - 5, 5, DataNode, ( + min_count=5, + max_count=5, + types=DataNode, + arg_names=( ("from", "frompos", "len", "to", "topos"), ) ), @@ -1688,7 +2226,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("x", "s"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("x", "s"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1698,7 +2240,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("c"),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("c"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1708,7 +2254,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1719,7 +2269,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("x",), ("x", "dim") ) @@ -1733,7 +2286,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1743,7 +2300,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={"mold": DataNode}, return_type=None, reference_accesses=None, @@ -1754,7 +2315,11 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, # Argnames depends on the input. - required_args=ArgDesc(0, 1, DataNode, (("",),)), + required_args=ArgDesc( + min_count=0, + max_count=1, + types=DataNode, + arg_names=(("",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1764,7 +2329,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("x", "mold",),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("x", "mold",),)), optional_args={"round": DataNode}, return_type=None, reference_accesses=None, @@ -1774,7 +2343,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("array", "mask"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("array", "mask"),)), optional_args={"vector": DataNode}, return_type=None, reference_accesses=None, @@ -1785,7 +2358,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("mask",), ("mask", "dim") ) @@ -1799,7 +2375,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1809,7 +2389,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1819,7 +2403,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1829,7 +2417,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1840,7 +2432,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -1854,7 +2449,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1864,8 +2463,12 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, - (("repeatable", "image_distinct"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("repeatable", "image_distinct"),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -1875,7 +2478,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference, (("harvest",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("harvest",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1885,7 +2492,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, Reference, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=Reference, + arg_names=()), optional_args={"size": DataNode, "put": DataNode, "Get": DataNode}, return_type=None, reference_accesses=None, @@ -1895,7 +2506,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, Reference, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1905,7 +2520,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, Reference, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("a",),)), optional_args={}, return_type=None, reference_accesses=None @@ -1915,7 +2534,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -1926,7 +2549,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 2, 3, DataNode, ( + min_count=2, + max_count=3, + types=DataNode, + arg_names=( ("array", "operation"), ("array", "operation", "dim") ) @@ -1942,7 +2568,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("string", "ncopies"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("string", "ncopies"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1952,7 +2582,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("source", "shape"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("source", "shape"),)), optional_args={"pad": DataNode, "order": DataNode}, return_type=None, reference_accesses=None, @@ -1962,7 +2596,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1972,7 +2610,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(2, 2, Reference, (("a", "b"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("a", "b"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1982,7 +2624,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("x", "i"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("x", "i"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -1992,7 +2638,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("string", "set"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("string", "set"),)), optional_args={"back": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2002,7 +2652,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference, (("name",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("name",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2012,7 +2666,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, Reference, (("r",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("r",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2022,7 +2680,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, Reference, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=Reference, + arg_names=()), optional_args={"P": DataNode, "R": DataNode, "radix": DataNode}, return_type=None, reference_accesses=None, @@ -2032,7 +2694,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("x", "i"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("x", "i"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2042,7 +2708,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, Reference, (("source",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=Reference, + arg_names=(("source",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -2052,7 +2722,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("i", "shift"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2062,7 +2736,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("i", "shift"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2072,7 +2750,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, Reference, (("i", "shift"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=Reference, + arg_names=(("i", "shift"),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2082,7 +2764,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("a", "b"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("a", "b"),)), optional_args={}, return_type=None, reference_accesses=None @@ -2092,7 +2778,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2102,7 +2792,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2112,7 +2806,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("array",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("array",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2122,7 +2820,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2132,8 +2834,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(3, 3, DataNode, ( - ("source", "dim", "ncopies"),) + required_args=ArgDesc( + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("source", "dim", "ncopies"),) ), optional_args={}, return_type=None, @@ -2144,7 +2849,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2154,7 +2863,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={"team": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2164,7 +2877,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("a",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("a",),)), optional_args={"kind": DataNode}, return_type=None, reference_accesses=None, @@ -2175,7 +2892,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 1, 2, DataNode, ( + min_count=1, + max_count=2, + types=DataNode, + arg_names=( ("array",), ("array", "dim") ) @@ -2189,7 +2909,11 @@ class Intrinsic(IAttr, Enum): is_pure=False, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={"count": DataNode, "count_rate": DataNode, "count_max": DataNode}, @@ -2201,7 +2925,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2211,7 +2939,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("x",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2221,7 +2953,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(0, 0, DataNode, ()), + required_args=ArgDesc( + min_count=0, + max_count=0, + types=DataNode, + arg_names=()), optional_args={"team": DataNode}, return_type=None, reference_accesses=None, @@ -2232,7 +2968,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 0, 2, DataNode, ( + min_count=0, + max_count=2, + types=DataNode, + arg_names=( (), ("coarray",), ("coarray", "dim") @@ -2247,7 +2986,12 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, (Reference, Literal), (("x",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=(Reference, Literal), + arg_names=(("x",),) + ), optional_args={}, return_type=None, reference_accesses=None, @@ -2257,7 +3001,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("i",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("i",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2267,7 +3015,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("source", "mold"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("source", "mold"),)), optional_args={"size": DataNode}, return_type=None, reference_accesses=None, @@ -2277,7 +3029,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("matrix",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("matrix",),)), optional_args={}, return_type=None, reference_accesses=None, @@ -2287,7 +3043,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=False, - required_args=ArgDesc(1, 1, DataNode, (("string",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("string",),)), optional_args={}, return_type=None, reference_accesses=None @@ -2297,7 +3057,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("array",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("array",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2307,7 +3071,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=False, is_inquiry=True, - required_args=ArgDesc(1, 1, DataNode, (("coarray",),)), + required_args=ArgDesc( + min_count=1, + max_count=1, + types=DataNode, + arg_names=(("coarray",),)), optional_args={"dim": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2318,7 +3086,10 @@ class Intrinsic(IAttr, Enum): is_elemental=False, is_inquiry=False, required_args=ArgDesc( - 3, 3, DataNode, (("vector", "mask", "field"),) + min_count=3, + max_count=3, + types=DataNode, + arg_names=(("vector", "mask", "field"),) ), optional_args={}, return_type=None, @@ -2329,7 +3100,11 @@ class Intrinsic(IAttr, Enum): is_pure=True, is_elemental=True, is_inquiry=False, - required_args=ArgDesc(2, 2, DataNode, (("string", "set"),)), + required_args=ArgDesc( + min_count=2, + max_count=2, + types=DataNode, + arg_names=(("string", "set"),)), optional_args={"back": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, @@ -2406,10 +3181,20 @@ def canonicalise(self): :raises NotImplementedError: If there is argument ambiguity and canonicalisation is not possible. ''' + # First step is to convert all the argument names in the + # intrinsic call to lower case. This also avoids constant + # need to convert argument names to lower case when doing + # comparisons. + argument_names = self.argument_names + for i, name in enumerate(argument_names): + if name: + self._argument_names[i] = (self._argument_names[i][0], + name.lower()) + # Get the optional argument names optional_names = list(self.intrinsic.optional_args.keys()) - # If PSyclone can't handle the required args arg_names due + # If PSyclone can't handle the required args due # to them being non-finite or context sensitive, then skip # checking argument names (This is if [0][0] is None or ''). if (not (len(self.intrinsic.required_args.arg_names) == 1 and @@ -2452,59 +3237,64 @@ def canonicalise(self): len(optional_names))): raise ValueError( f"Found too many arguments when canonicalising the " - f"'{self.intrinsic.name}' IntrinsicCall. Requires at " - f"most {self.intrinsic.required_args.max_count} " - f"arguments but found {len(self.arguments)}." + f"'{self.intrinsic.name}' IntrinsicCall. Requires at most " + f"{self.intrinsic.required_args.max_count + + len(optional_names)}" + f" arguments but found {len(self.arguments)}." ) - # Find which argument list we are canonicalising + # Find which intrinsic call interface we are canonicalising with. if len(self.intrinsic.required_args.arg_names) > 1: - available_args = list(range(len( - self.intrinsic.required_args.arg_names - ))) - # Discount any of the required argument lists that don't contain - # a named non-optional argument + # Create a list of all the possible interface's argument lists. + available_args = [ + names for names in self.intrinsic.required_args.arg_names + ] + # Remove any of the interfaces that don't contain + # a named non-optional argument from the list of potential + # candidate interfaces. for name in self.argument_names: if not name: continue + # Optional argument names are skipped over as they don't + # affect which interface is being used. if name in optional_names: continue - for i, arglist in enumerate( - self.intrinsic.required_args.arg_names): - if i not in available_args: - continue + for arglist in available_args: if name not in arglist: - available_args.remove(i) + available_args.remove(arglist) - # Remove any of the argument lists that we have too many or - # too few total arguments to handle. + # Remove any of the interfaces that we have too many or + # too few *total* arguments to be candidates. for choice in available_args[:]: - min_args = len(self.intrinsic.required_args.arg_names[choice]) + min_args = len(choice) max_args = min_args + len(optional_names) if (len(self.arguments) < min_args or len(self.arguments) > max_args): available_args.remove(choice) - # Remove any of the argument lists that we have too many or - # too few required arguments to handle. + # Remove any of the interfaces that we have too many or + # too few *required* arguments to be candidates. # At this point the total arguments must be valid for all # remaining choices, and all named arguments must also be # present. for choice in available_args[:]: - required_args = len( - self.intrinsic.required_args.arg_names[choice] - ) + required_args = len(choice) # Check if the number of unnamed arguments is greater # than the number of required arguments. If so then - # this choice is still acceptable. + # this choice is still acceptable (because optional + # arguments can also be positional). if (len([x for x in self.argument_names if x is None]) >= required_args): continue # Otherwise we need to check if all the # required arguments are present as named arguments. - remaining_required = self.intrinsic.required_args.\ - arg_names[choice][len([x for x in self.argument_names - if x is None]):] + # This operation pulls all the argument names from the + # potential interface that are not already matched to a + # positional argument in this IntrinsicCall. These must + # be matched to named arguments in this IntrinsicCall, else + # this interface cannot be a candidate for canonicalisation. + remaining_required = choice[len([ + x for x in self.argument_names if x is None]):] for name in remaining_required: if name not in self.argument_names: available_args.remove(choice) @@ -2519,9 +3309,7 @@ def canonicalise(self): f"argument set it should use. This can be resolved by " f"using named arguments in the Fortran source." ) - arg_list = self.intrinsic.required_args.arg_names[ - available_args[0] - ] + arg_list = available_args[0] elif len(self.intrinsic.required_args.arg_names) == 1: arg_list = self.intrinsic.required_args.arg_names[0] else: diff --git a/src/psyclone/tests/psyad/tl2ad_test.py b/src/psyclone/tests/psyad/tl2ad_test.py index a75176b6fe..e0174c5ba3 100644 --- a/src/psyclone/tests/psyad/tl2ad_test.py +++ b/src/psyclone/tests/psyad/tl2ad_test.py @@ -337,7 +337,6 @@ def test_get_active_variables_datatype_error(fortran_reader): with pytest.raises(NotImplementedError) as err: _get_active_variables_datatype(tl_psyir, ["a", "c"]) - print(str(err.value)) assert ("active variables of different datatype: 'a' is of intrinsic " "type 'Intrinsic.REAL' and precision 'Precision.UNDEFINED' while " "'c' is of intrinsic type 'Intrinsic.REAL' and precision " @@ -702,7 +701,6 @@ def test_generate_adjoint_test(fortran_reader, fortran_writer): harness = fortran_writer(test_psyir) assert (" real, dimension(npts) :: field\n" " real, dimension(npts) :: field_input" in harness) - print(harness) assert (" call random_number(field)\n" " field_input = field\n" "\n" diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index f1aed40679..f20b2d0bcb 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -210,12 +210,12 @@ def test_intrinsiccall_alloc_create(): IntrinsicCall.Intrinsic.ALLOCATE, [Reference(sym), ("Mold", Reference(bsym))]) assert isinstance(alloc, IntrinsicCall) - assert alloc.argument_names == [None, "Mold"] + assert alloc.argument_names == [None, "mold"] alloc = IntrinsicCall.create( IntrinsicCall.Intrinsic.ALLOCATE, [Reference(sym), ("Source", Reference(bsym)), ("stat", Reference(isym)), ("errmsg", Reference(csym))]) - assert alloc.argument_names == [None, "Source", "stat", "errmsg"] + assert alloc.argument_names == [None, "source", "stat", "errmsg"] def test_intrinsiccall_dealloc_create(): @@ -236,7 +236,7 @@ def test_intrinsiccall_dealloc_create(): dealloc = IntrinsicCall.create( IntrinsicCall.Intrinsic.DEALLOCATE, [Reference(sym), ("Stat", Reference(ierr))]) - assert dealloc.argument_names == [None, "Stat"] + assert dealloc.argument_names == [None, "stat"] def test_intrinsiccall_random_create(): @@ -618,7 +618,7 @@ def test_intrinsic_canonicalisation(fortran_reader): with pytest.raises(ValueError) as err: intrinsic.canonicalise() assert ("Found too many arguments when canonicalising the 'SUM' " - "IntrinsicCall. Requires at most 2 arguments but found 4." + "IntrinsicCall. Requires at most 3 arguments but found 4." in str(err.value)) # Test canonicalisation works if we have 1 argument for SUM. From a06be59ab3255008f8f8efb8fc93eeb3e54a71e7 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 3 Oct 2025 14:43:54 +0100 Subject: [PATCH 16/50] Documenting canonicalisation and fixing linting incompatibility between 3.9 and 3.12+ --- doc/developer_guide/psyir.rst | 10 ++++++++++ doc/developer_guide/transformations.rst | 2 +- src/psyclone/psyir/nodes/intrinsic_call.py | 9 ++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/doc/developer_guide/psyir.rst b/doc/developer_guide/psyir.rst index b9c9892990..3f5571aef8 100644 --- a/doc/developer_guide/psyir.rst +++ b/doc/developer_guide/psyir.rst @@ -594,6 +594,16 @@ available PSyIR `IntrinsicCall` match those of the `Fortran 2018 standard In addition to Fortran Intrinsics, special Fortran statements such as: `ALLOCATE`, `DEALLOCATE` and `NULLIFY` are also PSyIR IntrinsicCalls. +``IntrinsicCall`` nodes have a canonicalisation function, that is used +within PSyclone during their creation (via the ``IntrinsicCall.create`` +function). This attempts to match the Intrinsic and input arguments to +one of the interfaces for the intrinsic (as some intrinsics have multiple +possible argument interfaces). If the canonicalisation is successful, PSyclone +will convert all of the arguments to be named arguments, and reorder arguments +to match the specification from the Fortran standard. If canonicalisation +fails, then PSyclone will not create an ``IntrinsicCall`` corresponding to +the input. This canonicalisation is required to guarantee correct behaviour +when computing reference_accesses or the return type of an Intrinsic. IntrinsicCalls, like Calls, have properties to inform if the call is to a pure, elemental, inquiry (does not touch the first argument data) function diff --git a/doc/developer_guide/transformations.rst b/doc/developer_guide/transformations.rst index 37a0c0d844..1bc602e765 100644 --- a/doc/developer_guide/transformations.rst +++ b/doc/developer_guide/transformations.rst @@ -557,7 +557,7 @@ the ``ParallelLoopTrans`` class for reference): built upon the ``apply`` definition (e.g. ``LoopTrans`` has validation used for subclasses, but performs no actions in its newly added ``apply`` method). -3. The ``validate`` method should call the ``validate_options`` method on each of +3. The ``validate`` method should call the ``validate_options`` method on the keyword arguments and ``**kwargs``. This method should not be called on the ``options`` dictionary. The ``options`` input should overrule the keyword arguments when determining options to the apply and validate method. diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 9517d499e9..4cceb18825 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3236,11 +3236,10 @@ def canonicalise(self): if (len(self.arguments) > (self.intrinsic.required_args.max_count + len(optional_names))): raise ValueError( - f"Found too many arguments when canonicalising the " - f"'{self.intrinsic.name}' IntrinsicCall. Requires at most " - f"{self.intrinsic.required_args.max_count + - len(optional_names)}" - f" arguments but found {len(self.arguments)}." + f"""Found too many arguments when canonicalising the \ +'{self.intrinsic.name}' IntrinsicCall. Requires at most \ +{self.intrinsic.required_args.max_count + len(optional_names)} \ +arguments but found {len(self.arguments)}.""" ) # Find which intrinsic call interface we are canonicalising with. From 7dcdcbf7458dc721bbd5d96d1aabaaaf75c4187f Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Mon, 6 Oct 2025 15:51:50 +0100 Subject: [PATCH 17/50] Changes to address review comments --- src/psyclone/psyir/nodes/intrinsic_call.py | 213 ++++++++++-------- .../frontend/fparser2_where_handler_test.py | 1 - .../nodes/dynamic_omp_task_directive_test.py | 1 - .../tests/psyir/nodes/intrinsic_call_test.py | 114 +++++++--- .../transformations/acc_kernels_trans_test.py | 1 - 5 files changed, 199 insertions(+), 131 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 4cceb18825..42d3904fa2 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -42,6 +42,7 @@ from collections import namedtuple from collections.abc import Iterable from enum import Enum +from typing import List, Tuple from psyclone.core import AccessType, VariablesAccessMap from psyclone.psyir.nodes.call import Call @@ -3167,6 +3168,85 @@ def is_available_on_device(self, device_string: str = "") -> bool: f"Unsupported device_string value '{device_string}', the supported" " values are '' (default), 'nvfortran-all', 'nvfortran-uniform'") + def _find_matching_interface(self) -> Tuple[str]: + ''' + Finds the matching required argument interface for this node to + canonicalise to. + + :raises NotImplementedError: if there is not exactly one argument + interface that matches this + IntrinsicCall. + ''' + # Pull out the list of optional argument names. + optional_names = list(self.intrinsic.optional_args.keys()) + # Create a list of all the possible interface's argument lists. + potential_interfaces: List[Tuple[str]] = [ + names for names in self.intrinsic.required_args.arg_names + ] + # Remove any of the interfaces that don't contain + # a named non-optional argument from the list of potential + # candidate interfaces. + for name in self.argument_names: + if not name: + continue + # Optional argument names are skipped over as they don't + # affect which interface is being used. + if name in optional_names: + continue + for arglist in potential_interfaces: + if name not in arglist: + potential_interfaces.remove(arglist) + + # Remove any of the interfaces that have too many or + # too few *total* arguments to be candidates. + for choice in potential_interfaces[:]: + min_args = len(choice) + max_args = min_args + len(optional_names) + if (len(self.arguments) < min_args or + len(self.arguments) > max_args): + potential_interfaces.remove(choice) + + # Remove any of the interfaces that have too many or + # too few *required* arguments to be candidates. + # At this point the total arguments must be valid for all + # remaining choices, and all named arguments must also be + # present. + for choice in potential_interfaces[:]: + required_args = len(choice) + # Check if the number of unnamed arguments is greater + # than the number of required arguments. If so then + # this choice is still acceptable (because optional + # arguments can also be positional). + num_positional_arguments = len( + [x for x in self.argument_names if x is None] + ) + if num_positional_arguments >= required_args: + continue + # Otherwise we need to check if all the + # required arguments are present as named arguments. + # This operation pulls all the argument names from the + # potential interface that are not already matched to a + # positional argument in this IntrinsicCall. These must + # be matched to named arguments in this IntrinsicCall, else + # this interface cannot be a candidate for canonicalisation. + remaining_required = choice[num_positional_arguments:] + for name in remaining_required: + if name not in self.argument_names: + potential_interfaces.remove(choice) + break + + # If we didn't reduce the number of potential interfacfes to a + # single interface then we can't canonicalise. + if (len(potential_interfaces) > 1 or + len(potential_interfaces) == 0): + raise NotImplementedError( + f"Cannot canonicalise '{self.intrinsic.name}' " + f"IntrinsicCall as PSyclone can't determine which " + f"argument set it should use. This can be resolved by " + f"using named arguments in the Fortran source." + ) + return potential_interfaces[0] + def canonicalise(self): '''Canonicalise an IntrinsicCall in the PSyIR. Upon successful canonicalisation, all arguments will become named arguments and @@ -3205,7 +3285,7 @@ def canonicalise(self): name in tupl ] all_valid_names.extend(optional_names) - # Check we have valid argument names. + # Check that all arguments names provided to this call are valid. # Raise ValueError if not. for name in self.argument_names: if not name: @@ -3218,7 +3298,7 @@ def canonicalise(self): f"'{sorted(set(all_valid_names))}'." ) - # Check we have a valid number of arguments + # Check that this call has a valid number of arguments if len(self.arguments) < self.intrinsic.required_args.min_count: raise ValueError( f"Found too few arguments when canonicalising the " @@ -3235,89 +3315,29 @@ def canonicalise(self): if (len(self.arguments) > (self.intrinsic.required_args.max_count + len(optional_names))): + max_args = (self.intrinsic.required_args.max_count + + len(optional_names)) raise ValueError( - f"""Found too many arguments when canonicalising the \ -'{self.intrinsic.name}' IntrinsicCall. Requires at most \ -{self.intrinsic.required_args.max_count + len(optional_names)} \ -arguments but found {len(self.arguments)}.""" + f"Found too many arguments when canonicalising the " + f"'{self.intrinsic.name}' IntrinsicCall. Requires at most " + f"{max_args} arguments but found {len(self.arguments)}." ) # Find which intrinsic call interface we are canonicalising with. if len(self.intrinsic.required_args.arg_names) > 1: - # Create a list of all the possible interface's argument lists. - available_args = [ - names for names in self.intrinsic.required_args.arg_names - ] - # Remove any of the interfaces that don't contain - # a named non-optional argument from the list of potential - # candidate interfaces. - for name in self.argument_names: - if not name: - continue - # Optional argument names are skipped over as they don't - # affect which interface is being used. - if name in optional_names: - continue - for arglist in available_args: - if name not in arglist: - available_args.remove(arglist) - - # Remove any of the interfaces that we have too many or - # too few *total* arguments to be candidates. - for choice in available_args[:]: - min_args = len(choice) - max_args = min_args + len(optional_names) - if (len(self.arguments) < min_args or - len(self.arguments) > max_args): - available_args.remove(choice) - - # Remove any of the interfaces that we have too many or - # too few *required* arguments to be candidates. - # At this point the total arguments must be valid for all - # remaining choices, and all named arguments must also be - # present. - for choice in available_args[:]: - required_args = len(choice) - # Check if the number of unnamed arguments is greater - # than the number of required arguments. If so then - # this choice is still acceptable (because optional - # arguments can also be positional). - if (len([x for x in self.argument_names if x is None]) >= - required_args): - continue - # Otherwise we need to check if all the - # required arguments are present as named arguments. - # This operation pulls all the argument names from the - # potential interface that are not already matched to a - # positional argument in this IntrinsicCall. These must - # be matched to named arguments in this IntrinsicCall, else - # this interface cannot be a candidate for canonicalisation. - remaining_required = choice[len([ - x for x in self.argument_names if x is None]):] - for name in remaining_required: - if name not in self.argument_names: - available_args.remove(choice) - break - - # If we still have more than one available argument list here - # then we can't canonicalise - if len(available_args) > 1 or len(available_args) == 0: - raise NotImplementedError( - f"Cannot canonicalise '{self.intrinsic.name}' " - f"IntrinsicCall as PSyclone can't determine which " - f"argument set it should use. This can be resolved by " - f"using named arguments in the Fortran source." - ) - arg_list = available_args[0] + interface_arg_names = self._find_matching_interface() elif len(self.intrinsic.required_args.arg_names) == 1: - arg_list = self.intrinsic.required_args.arg_names[0] + # This intrinsic only has a single possible interface. + interface_arg_names = self.intrinsic.required_args.arg_names[0] else: - arg_list = () - - # Handle cases where None or "" is in the arg_list, as this implies - # context sensitive argument naming which PSyclone cannot handle. - if arg_list and not arg_list[0]: - # If we find any named non-optional name arguments for these + # This intrinsic has no required arguments. + interface_arg_names = () + + # Handle cases where None or "" is in the interface_arg_names, + # as this implies context sensitive argument naming which PSyclone + # cannot handle. + if interface_arg_names and not interface_arg_names[0]: + # If we find any named non-optional named arguments for these # intrinsics then we can't canonicalise this IntrinsicCall. # N.B. With currently supported intrinsic there are no # optional argument on these context-sensitive intrinsics @@ -3333,7 +3353,8 @@ def canonicalise(self): f"Cannot canonicalise '{self.intrinsic.name}' " f"as non-optional argument name '{name}' found " f"but the Intrinsic has context-sensitive argument " - f"names which is unsupported by PSyclone." + f"names which is unsupported by PSyclone. Supplied " + f"intrinsic was '{self.debug_string().rstrip()}'." ) # The following rules are defined by the Fortran standard. @@ -3344,25 +3365,33 @@ def canonicalise(self): # 3. All unnamed arguments will occur before any named arguments. # Name any unnamed arguments. - for i, arg in enumerate(self.argument_names): + for i, name in enumerate(self.argument_names): # If we find a named arg then we can exit this section. - if arg: + if name: break - if i < len(arg_list): + if i < len(interface_arg_names): # We found a required argument without a name. + # Update the argument_names tuple with the corresponding + # name from the matched interface. self._argument_names[i] = (self._argument_names[i][0], - arg_list[i]) + interface_arg_names[i]) continue # Otherwise we found an optional argument, which will always # be in order if unnamed. self._argument_names[i] = (self._argument_names[i][0], - optional_names[i - len(arg_list)]) + optional_names[i - len( + interface_arg_names)]) # We have all arguments named now, we want to reorder them. new_arg_names = [] new_args = [] - for required in arg_list: + for required in interface_arg_names: + # Skip over required argument names when they're either None or '' + # as these designate arguments PSyclone cannoot canonicalise to + # names. + if not required: + continue index = self.argument_names.index(required) new_arg_names.append(self._argument_names[index]) new_args.append(self.arguments[index]) @@ -3376,7 +3405,7 @@ def canonicalise(self): # Replace the argument list with the canonicalised version. if len(new_args) > 0: - for child in self.children[1:]: + for child in self.arguments: child.detach() for child in new_args: self.addchild(child) @@ -3462,15 +3491,17 @@ def create(cls, intrinsic, arguments=()): # the intrinsic enum. call._add_args(call, arguments) - # Error check and canoniclise the call + # Error check and canonicalise the call try: call.canonicalise() - except (ValueError, NotImplementedError) as err: + except (ValueError, NotImplementedError): + # Since we fail canonicalisation, we need to undo any links + # created between nodes and return all inputs to their original + # state before raising the error to the caller. for child in call.children: child.detach() - # Rereaise the error with the same type and message as the - # original error. - raise type(err)(err.args[0]) from err + # Rereaise the error. + raise return call diff --git a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py index 5c3aa31397..221fc9c88d 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py @@ -496,7 +496,6 @@ def test_where_containing_sum_no_dim(fortran_reader, fortran_writer): routine = psyir.walk(Routine)[0] assert isinstance(routine[0], Loop) output = fortran_writer(psyir) - print(output) assert ("SUM(array=a_i_last_couple) / picefr(LBOUND(array=picefr, dim=1) " "+ widx1 - 1,LBOUND(array=picefr, dim=2) + widx2 - 1)" in output) diff --git a/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py b/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py index 55c7ba18dd..5b3b3eb870 100644 --- a/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py +++ b/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py @@ -2842,7 +2842,6 @@ def test_omp_task_directive_nemolite_boundary( enddo enddo !$omp end task''' - print(fortran_writer(tree)) assert correct in fortran_writer(tree) assert Compile(tmpdir).string_compiles(fortran_writer(tree)) diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index f20b2d0bcb..cceedeb32d 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -585,9 +585,10 @@ def test_verify_intrinsic(fortran_reader, fortran_writer): "back=.true., kind=kind(x=1)) == 0) then" in result) -def test_intrinsic_canonicalisation(fortran_reader): +def test_intrinsic_canonicalisation_value_errors(): ''' - Test the canonicalisation function of the IntrinsicCall class. + Test the canonicalisation function of the IntrinsicCall class raises + ValueErrors with bad inputs. ''' # Test canonicalisation fails if we have an incorrect name argument. @@ -621,12 +622,12 @@ def test_intrinsic_canonicalisation(fortran_reader): "IntrinsicCall. Requires at most 3 arguments but found 4." in str(err.value)) - # Test canonicalisation works if we have 1 argument for SUM. - intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) - intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.canonicalise() - assert intrinsic.argument_names[0] == "array" +def test_intrinsic_canonicalisation_not_implemented_errors(): + ''' + Test the canonicalisation function of the IntrinsicCall class raises + ValueErrors for Intrinsic structures PSyclone can't handle. + ''' # Test canonicalisation doesn't work when we have 2 arguments for SUM # with no naming, as it can't determine between the SUM variants. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) @@ -639,6 +640,50 @@ def test_intrinsic_canonicalisation(fortran_reader): "resolved by using named arguments in the Fortran source." in str(err.value)) + # The only case I can see that can hit line 2473 + # (i not in available args: continue) is an invalid BESSEL_JN Intrinsic + # This is future-proofing for context-sensitive argument handling. + # TODO #2302 + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.BESSEL_JN) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + # Set up the argument_names array and set the argument names + intrinsic.argument_names + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "n1") + intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "n2") + with pytest.raises(NotImplementedError) as err: + intrinsic.canonicalise() + assert ("Cannot canonicalise 'BESSEL_JN' IntrinsicCall as PSyclone can't " + "determine which argument set it should use. This can be resolved " + "by using named arguments in the Fortran source" + in str(err.value)) + + # Test we get the expected error when non-optional argument names are + # passed to an intrinsic where PSyclone can't handle the required argument + # names. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.argument_names + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "array") + with pytest.raises(NotImplementedError) as err: + intrinsic.canonicalise() + assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " + "'array' found but the Intrinsic has context-sensitive argument " + "names which is unsupported by PSyclone. Supplied intrinsic was " + "'ALLOCATED(array=a)'." in str(err.value)) + + +def test_canonicalisation(): + ''' + Test that the canonicalisation function works as expected for + cases that can be canonicalised. + ''' + # Test canonicalisation works if we have 1 argument for SUM. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.canonicalise() + assert intrinsic.argument_names[0] == "array" + # Test canonicalisation does work when we give a name to the 2nd argument. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) @@ -649,7 +694,7 @@ def test_intrinsic_canonicalisation(fortran_reader): intrinsic.canonicalise() assert intrinsic.argument_names == ["array", "mask"] - # Test we only ge the correct canonicalisation when we have a named + # Test that the correct canonicalisation is performed when we have a named # argument only in one of the lists. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) @@ -658,24 +703,27 @@ def test_intrinsic_canonicalisation(fortran_reader): intrinsic.argument_names intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "dim") intrinsic.canonicalise() + assert intrinsic.argument_names[0] == "array" + assert intrinsic.argument_names[1] == "dim" - # The only case I can see that can hit line 2473 - # (i not in available args: continue) is an invalid BESSEL_JN Intrinsic - # I think at the moment, though if we can support context specific - # names we do need that. - intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.BESSEL_JN) - intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) - # Set up the argument_names array and set the argument names + # Test that canonicalisation works when optional arguments appear first + # when all arguments are named. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + a_arg = Reference(DataSymbol("a", INTEGER_TYPE)) + b_arg = Reference(DataSymbol("b", INTEGER_TYPE)) + intrinsic.addchild(a_arg) + intrinsic.addchild(b_arg) + # Set up the argument_names array and set the first ones name to be mask + # (optional) and the second to be array. intrinsic.argument_names - intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "n1") - intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "n2") - with pytest.raises(NotImplementedError) as err: - intrinsic.canonicalise() - assert ("Cannot canonicalise 'BESSEL_JN' IntrinsicCall as PSyclone can't " - "determine which argument set it should use. This can be resolved " - "by using named arguments in the Fortran source" - in str(err.value)) + intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "mask") + intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "array") + intrinsic.canonicalise() + # Canonicalisation should have the names as array then mask, and have + # reversed the argument order to match the movement of the argument names. + assert intrinsic.argument_names == ["array", "mask"] + assert intrinsic.arguments[0] is b_arg + assert intrinsic.arguments[1] is a_arg # Check we can canonicalise an intrinsic with only one argument set. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SIN) @@ -701,8 +749,8 @@ def test_intrinsic_canonicalisation(fortran_reader): assert intrinsic.children[2].symbol.name == "a" assert intrinsic.children[3].symbol.name == "b" - # Test canonicliation for intrinsic when PSyclone doesn't support named - # non-optional arguments. + # Test canonicliation for intrinsic when PSyclone can't + # canonicalise the names of non-optional arguments. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATE) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) @@ -712,16 +760,6 @@ def test_intrinsic_canonicalisation(fortran_reader): intrinsic.canonicalise() assert intrinsic.argument_names == [None, None, "mold"] - intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) - intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.argument_names - intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "array") - with pytest.raises(NotImplementedError) as err: - intrinsic.canonicalise() - assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " - "'array' found but the Intrinsic has context-sensitive argument " - "names which is unsupported by PSyclone." in str(err.value)) - # Check that we canoncalise when we have unnamed optional arguments. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) @@ -730,8 +768,10 @@ def test_intrinsic_canonicalisation(fortran_reader): intrinsic.canonicalise() assert intrinsic.argument_names == ["array", "dim", "mask"] + print("-------------") # Check that we don't fail when the required argument name is None - # and no argument name is supported. + # and no argument name can be generated by PSyclone. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.canonicalise() + assert intrinsic.argument_names == [None] diff --git a/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py b/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py index 2d7bffa1ad..88371221ff 100644 --- a/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py @@ -303,7 +303,6 @@ def test_kernels_around_where_stmt(fortran_reader, fortran_writer): schedule = psyir.walk(Routine)[0] acc_trans = ACCKernelsTrans() acc_trans.apply([schedule[1]]) - print(fortran_writer(psyir)) assert (" a(:,:) = 1.0\n" " !$acc kernels\n" " do widx2 = 1, SIZE(array=a(:,:), dim=2), 1\n" From b530cdc2eda3a76569fa7a9222bd8ca81fbbc618 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 11:03:21 +0100 Subject: [PATCH 18/50] Removed keyword arguments from FLOAT --- src/psyclone/psyir/nodes/intrinsic_call.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 42d3904fa2..9fefa9b11a 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -1310,7 +1310,9 @@ class Intrinsic(IAttr, Enum): min_count=1, max_count=1, types=DataNode, - arg_names=(("i",),)), + # FLOAT is a language extension, and not all compilers + # (e.g. nvfortran) can handle a keyword argument. + arg_names=(("",),)), optional_args={}, return_type=None, reference_accesses=None, From 18ab68e2c06107095d9e74748c9b83add0e5b30f Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 13:47:40 +0100 Subject: [PATCH 19/50] Fix for NEMO SIGN overriding --- src/psyclone/psyir/nodes/intrinsic_call.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 9fefa9b11a..3cfe7deb8b 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2771,7 +2771,11 @@ class Intrinsic(IAttr, Enum): min_count=2, max_count=2, types=DataNode, - arg_names=(("a", "b"),)), + # SIGN has two arguments, a and b, however since NEMO + # overrides this function and doesn't supported named + # arguments, PSyclone specifically doesn't canonicalise + # the argument names. + arg_names=(("",),)), optional_args={}, return_type=None, reference_accesses=None From e00e779d8a1b97c4524a8ff9d9119eca0c669267 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 13:52:07 +0100 Subject: [PATCH 20/50] Added a TODO for NEMO SIGN --- src/psyclone/psyir/nodes/intrinsic_call.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 3cfe7deb8b..b85fb80998 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2771,8 +2771,8 @@ class Intrinsic(IAttr, Enum): min_count=2, max_count=2, types=DataNode, - # SIGN has two arguments, a and b, however since NEMO - # overrides this function and doesn't supported named + # TODO #2102 SIGN has two arguments, a and b, however since + # NEMO overrides this function and doesn't support named # arguments, PSyclone specifically doesn't canonicalise # the argument names. arg_names=(("",),)), From 36426fbc27a2d00c6357c8401603d80b8c49ad62 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 14:06:42 +0100 Subject: [PATCH 21/50] Fixed error in SIGN --- src/psyclone/psyir/nodes/intrinsic_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index b85fb80998..716db90e46 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2775,7 +2775,7 @@ class Intrinsic(IAttr, Enum): # NEMO overrides this function and doesn't support named # arguments, PSyclone specifically doesn't canonicalise # the argument names. - arg_names=(("",),)), + arg_names=(("",""),)), optional_args={}, return_type=None, reference_accesses=None From a8c13b6f243469f4c78c73cf233dd59aa2117a12 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 14:07:04 +0100 Subject: [PATCH 22/50] linting fixW --- src/psyclone/psyir/nodes/intrinsic_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 716db90e46..3cdba8ae8a 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2775,7 +2775,7 @@ class Intrinsic(IAttr, Enum): # NEMO overrides this function and doesn't support named # arguments, PSyclone specifically doesn't canonicalise # the argument names. - arg_names=(("",""),)), + arg_names=(("", ""),)), optional_args={}, return_type=None, reference_accesses=None From 67a161b4f1453817f30df144d6b2217cfdfa8a48 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 14:31:39 +0100 Subject: [PATCH 23/50] Fix failing tests --- .../tests/domain/lfric/lfric_builtins_test.py | 4 ++-- .../frontend/fparser2_intrinsic_handler_test.py | 2 +- .../intrinsics/sign2code_trans_test.py | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py index 99a83eb327..68b91d2e88 100644 --- a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py @@ -1654,7 +1654,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(a=a, b=f1_data(df))\n") in code + "f2_data(df) = SIGN(a, f1_data(df))\n") in code # Also with a literal kern = builtin_from_file("15.10.2_sign_X_builtin_set_by_value.f90") @@ -1665,7 +1665,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(a=-2.0_r_def, b=f1_data(df))\n" in code) + "f2_data(df) = SIGN(-2.0_r_def, f1_data(df))\n" in code) # The integer version has the datatype changed to integer in the metadata # and string representation diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index 478450ce61..af76bdefcb 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -193,7 +193,7 @@ def test_intrinsic_handler_intrinsiccall_onearg( ('x = min(a, b, c)', IntrinsicCall.Intrinsic.MIN, [None, None, None]), ('x = sign(a, b)', IntrinsicCall.Intrinsic.SIGN, - ["a", "b"]), + [None, None]), ('x = sqrt(a)', IntrinsicCall.Intrinsic.SQRT, ["x"]), ('x = aimag(a)', IntrinsicCall.Intrinsic.AIMAG, diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py index 57f4eff6a1..8d85fa05f9 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py @@ -61,7 +61,7 @@ def example_psyir(create_expression): '''Utility function that creates a PSyIR tree containing a SIGN intrinsic and returns it. - :param function create_expression: function used to create the \ + :param function create_expression: function used to create the content of the first argument of the SIGN intrinsic. :returns: PSyIR SIGN intrinsic instance. @@ -107,7 +107,7 @@ def test_correct(func, output, tmpdir): f" real, intent(inout) :: arg\n" f" real, intent(inout) :: arg_1\n" f" real :: psyir_tmp\n\n" - f" psyir_tmp = SIGN(a={output}, b=arg_1)\n\n" + f" psyir_tmp = SIGN({output}, arg_1)\n\n" f"end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -161,7 +161,7 @@ def test_correct_expr(tmpdir): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = 1.0 + SIGN(a=arg * 3.14, b=arg_1) + 2.0\n\n" + " psyir_tmp = 1.0 + SIGN(arg * 3.14, arg_1) + 2.0\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -215,7 +215,7 @@ def test_correct_2sign(tmpdir, fortran_writer): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = SIGN(a=1.0, b=1.0) + SIGN(a=arg * 3.14, b=arg_1)\n\n" + " psyir_tmp = SIGN(1.0, 1.0) + SIGN(arg * 3.14, arg_1)\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call, root.symbol_table) @@ -271,7 +271,7 @@ def test_sign_with_integer_arg(fortran_reader, fortran_writer, tmpdir): program test_prog integer, parameter :: idef = kind(1) integer(idef) :: my_arg, other_arg - my_arg = SIGN(a=my_arg, b=other_arg) + my_arg = SIGN(my_arg, other_arg) end program test_prog''' psyir = fortran_reader.psyir_from_source(code) trans = Sign2CodeTrans() @@ -308,8 +308,8 @@ def test_sign_of_unknown_type(fortran_reader): if call.intrinsic.name == "SIGN"] with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[0]) - assert ("Sign2CodeTrans cannot be applied to 'SIGN(a=MAX(ABS(a=ztmp1), " - "1.e-6_wp), b=ztmp1) because the type of the argument" + assert ("Sign2CodeTrans cannot be applied to 'SIGN(MAX(ABS(a=ztmp1), " + "1.e-6_wp), ztmp1) because the type of the argument" in str(err.value)) with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[1]) From d91c9cc071043e7f5716e6d95b7c8869bbabcbdb Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 7 Oct 2025 15:41:33 +0100 Subject: [PATCH 24/50] Added argument_by_name to call --- src/psyclone/psyir/nodes/call.py | 14 +++++++++++++- src/psyclone/tests/psyir/nodes/call_test.py | 11 +++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py index 6ca5b9f416..561b9dff39 100644 --- a/src/psyclone/psyir/nodes/call.py +++ b/src/psyclone/psyir/nodes/call.py @@ -37,7 +37,7 @@ ''' This module contains the Call node implementation.''' from collections.abc import Iterable -from typing import List, Tuple +from typing import List, Tuple, Union from psyclone.configuration import Config from psyclone.core import AccessType, VariablesAccessMap @@ -378,6 +378,18 @@ def arguments(self) -> Tuple[DataNode]: return tuple(self.children[1:]) return () + def argument_by_name(self, name: str) -> Union[DataNode, None]: + ''' + :param name: The name of the argument to lookup. + + :returns: The argument specified with the input name, or None if its + not present. + ''' + arg_names = self.argument_names + if name not in arg_names: + return None + return self.arguments[arg_names.index(name)] + @property def is_elemental(self): ''' diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py index da334ad00c..f447a863f5 100644 --- a/src/psyclone/tests/psyir/nodes/call_test.py +++ b/src/psyclone/tests/psyir/nodes/call_test.py @@ -368,6 +368,17 @@ def test_call_replacenamedarg(): assert call._argument_names[1][0] == id(op2) +def test_call_argument_by_name(): + '''Test the argument_by_name method.''' + op1 = Literal("1", INTEGER_TYPE) + op2 = Literal("2", INTEGER_TYPE) + op3 = Literal("3", INTEGER_TYPE) + call = Call.create(RoutineSymbol("hello"), [op1, ("a", op2), ("b", op3)]) + assert call.argument_by_name("z") is None + assert call.argument_by_name("a") is op2 + assert call.argument_by_name("b") is op3 + + def test_call_reference_accesses(): '''Test the reference_accesses() method.''' rsym = RoutineSymbol("trillian") From 38b267b609a470d8098ea0882512e2ee95480ad2 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 8 Oct 2025 10:34:47 +0100 Subject: [PATCH 25/50] Revert "Fix for NEMO SIGN overriding" This reverts commit 18ab68e2c06107095d9e74748c9b83add0e5b30f. --- src/psyclone/psyir/nodes/intrinsic_call.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 3cdba8ae8a..9fefa9b11a 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2771,11 +2771,7 @@ class Intrinsic(IAttr, Enum): min_count=2, max_count=2, types=DataNode, - # TODO #2102 SIGN has two arguments, a and b, however since - # NEMO overrides this function and doesn't support named - # arguments, PSyclone specifically doesn't canonicalise - # the argument names. - arg_names=(("", ""),)), + arg_names=(("a", "b"),)), optional_args={}, return_type=None, reference_accesses=None From a1c480d35a6f059f439c7c7f6221bc9a4c8c4842 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 8 Oct 2025 15:14:57 +0100 Subject: [PATCH 26/50] Adds switches to change the output behaviour for intrinsics from the input --- src/psyclone/configuration.py | 42 +++++++++++++++++++ src/psyclone/generator.py | 35 ++++++++++++++++ src/psyclone/psyir/backend/fortran.py | 11 +++++ src/psyclone/psyir/nodes/intrinsic_call.py | 30 ++++++++++++- src/psyclone/tests/configuration_test.py | 12 ++++++ src/psyclone/tests/generator_test.py | 31 ++++++++++++++ .../tests/psyir/backend/fortran_test.py | 38 +++++++++++++++++ .../fparser2_intrinsic_handler_test.py | 2 +- .../tests/psyir/nodes/intrinsic_call_test.py | 22 +++++++++- 9 files changed, 220 insertions(+), 3 deletions(-) diff --git a/src/psyclone/configuration.py b/src/psyclone/configuration.py index c11c64fa70..420574f859 100644 --- a/src/psyclone/configuration.py +++ b/src/psyclone/configuration.py @@ -235,6 +235,11 @@ def __init__(self): # The Fortran standard that fparser should use self._fortran_standard = None + # By default, the PSyIR backends output argument names on (most) + # IntrinsicCalls. These two options enable control of tha behaviour. + self._sign_intrinsic_kwargs = False + self._intrinsic_kwargs = True + # ------------------------------------------------------------------------- def load(self, config_file=None): '''Loads a configuration file. @@ -771,6 +776,43 @@ def get_constants(self): ''' return self.api_conf().get_constants() + @property + def intrinsic_kwargs(self) -> bool: + ''' + :returns: whether the output of intrinsic named arguments is + enabled for required intrinsic arguments. + ''' + return self._intrinsic_kwargs + + @intrinsic_kwargs.setter + def intrinsic_kwargs(self, output_kwargs: bool) -> None: + ''' + Setter for whether the backend should output required argument names + on IntrinsicCalls. + + :param output_kwargs: whether to output required argument names. + ''' + self._intrinsic_kwargs = output_kwargs + + @property + def sign_intrinsic_kwargs(self) -> bool: + ''' + :returns: whether the output of the sign intrinsic should have + named arguments. + ''' + return self._sign_intrinsic_kwargs + + @sign_intrinsic_kwargs.setter + def sign_intrinsic_kwargs(self, output_kwargs: bool) -> None: + ''' + Setter for whether the backend should output argument names on the + SIGN IntrinsicCall. + + :param output_kwargs: whether to output argument names on SIGN + intrinsics. + ''' + self._sign_intrinsic_kwargs = output_kwargs + # ============================================================================= class BaseConfig: diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index 1f2dce1a86..ec744e7fa7 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -574,6 +574,28 @@ def main(arguments): "(default is to look at the input file extension)." ) + intrinsic_output_group = parser.add_argument_group( + "Fortran Intrinsic output control", + "These settings control how PSyclone outputs Fortran Intrinsics. " + "The default behaviour is to output named arguments for all " + "intrinsics' arguments other than SIGN, which will not have " + "keyword arguments to support NEMO behaviour." + ) + intrinsic_output_group.add_argument( + "--disable-intrinsic-required-args", default=argparse.SUPPRESS, + action="store_true", + help="Disables output code containing argument names for an " + "intrinsic's required arguments, i.e. SUM(arr, mask=maskarr) " + "instead of (SUMarray=arr, mask=maskarr). This overrides any " + "other options specified for intrinsic output control." + ) + intrinsic_output_group.add_argument( + "--enable-sign-intrinsic-argnames", default=argparse.SUPPRESS, + action="store_true", + help="Enables adding argument names to the SIGN intrinsic's " + "arguments." + ) + args = parser.parse_args(arguments) # Set the logging system up. @@ -626,6 +648,19 @@ def main(arguments): api = args.psykal_dsl Config.get().api = api + # Record any intrinsic output format settings. + if "disable_intrinsic_required_args" in args: + Config.get().intrinsic_kwargs = False + if "enable_sign_intrinsic_argnames" in args: + if not Config.get().intrinsic_kwargs: + logger.info( + "Both the disable-intrinsic-required-args and the " + "enable-sign-intrinsic-argnames were specified. The " + "disable-intrinsic-required-args overrides other controls " + "so sign intrinsics won't have output argument names." + ) + Config.get().sign_intrinsic_kwargs = True + # Record any profiling options. if args.profile: try: diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index a00437d59d..8b4ecd9643 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -40,6 +40,7 @@ from a PSyIR tree. ''' # pylint: disable=too-many-lines +from psyclone.configuration import Config from psyclone.core import Signature from psyclone.errors import InternalError from psyclone.psyir.backend.language_writer import LanguageWriter @@ -1767,6 +1768,16 @@ def call_node(self, node) -> str: :returns: the equivalent Fortran code. ''' + # If its an IntrinsicCall then check the config to determine how + # we're outputting the result. + if isinstance(node, IntrinsicCall): + if not Config.get().intrinsic_kwargs: + # Remove argument names from required arguments. + node.remove_required_argument_names() + elif (node.intrinsic == IntrinsicCall.Intrinsic.SIGN and + not Config.get().sign_intrinsic_kwargs): + # Remove argument names from Sign. + node.remove_required_argument_names() args = self._gen_arguments(node) if isinstance(node, IntrinsicCall) and node.routine.name not in [ "DATE_AND_TIME", "SYSTEM_CLOCK", "MVBITS", "RANDOM_NUMBER", diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 9fefa9b11a..7df3ffd5b3 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -1748,7 +1748,7 @@ class Intrinsic(IAttr, Enum): min_count=2, max_count=2, types=DataNode, - arg_names=(("i", "shift"))), + arg_names=(("i", "shift"),)), optional_args={"size": DataNode}, return_type=None, reference_accesses=None, @@ -3249,6 +3249,34 @@ def _find_matching_interface(self) -> Tuple[str]: ) return potential_interfaces[0] + def remove_required_argument_names(self) -> None: + '''Remove the argument names from required arguments on this + IntrinsicCall if possible.''' + # First we need to ensure this is a canoncalised IntrinsicCall + try: + self.canonicalise() + except (ValueError, NotImplementedError): + # If this can't be canonicalised then don't change anything. + return + + # Find all of the required argument names available on this + # intrinsic. + all_required_names = [ + name for tupl in self.intrinsic.required_args.arg_names for + name in tupl + ] + new_arg_names = [] + for i, name in enumerate(self.argument_names): + if name in all_required_names: + new_arg_names.append( + (self._argument_names[i][0], None) + ) + else: + new_arg_names.append( + (self._argument_names[i][0], name) + ) + self._argument_names = new_arg_names + def canonicalise(self): '''Canonicalise an IntrinsicCall in the PSyIR. Upon successful canonicalisation, all arguments will become named arguments and diff --git a/src/psyclone/tests/configuration_test.py b/src/psyclone/tests/configuration_test.py index bd9858de87..cea567ec09 100644 --- a/src/psyclone/tests/configuration_test.py +++ b/src/psyclone/tests/configuration_test.py @@ -819,3 +819,15 @@ def test_fortran_standard(tmpdir): assert ("PSyclone configuration error: Invalid Fortran standard 'invalid' " "specified in config file. Must be one of['f2003', 'f2008']" in str(err.value)) + + +def test_intrinsic_settings(): + '''Test the getter and setter methods for intrinsic output control + in the config.''' + assert Config.get().intrinsic_kwargs is True + Config.get().intrinsic_kwargs = False + assert Config.get().intrinsic_kwargs is False + + assert Config.get().sign_intrinsic_kwargs is False + Config.get().sign_intrinsic_kwargs = True + assert Config.get().sign_intrinsic_kwargs is True diff --git a/src/psyclone/tests/generator_test.py b/src/psyclone/tests/generator_test.py index acbbcae881..980ac00ab5 100644 --- a/src/psyclone/tests/generator_test.py +++ b/src/psyclone/tests/generator_test.py @@ -2033,3 +2033,34 @@ def test_ignore_pattern(): mod_man = ModuleManager.get() assert mod_man._ignore_files == set(["abc1", "abc2"]) + + +def test_intrinsic_control_settings(tmpdir, caplog): + '''Checks that the intrinsic output control settings update the config + correctly''' + # Create dummy piece of code. + code = """program test + end program""" + filename = str(tmpdir.join("test.f90")) + with open(filename, "w", encoding='utf-8') as my_file: + my_file.write(code) + main([filename, "--disable-intrinsic-required-args"]) + assert Config.get().intrinsic_kwargs is False + + # Reset the config + Config.get().intrinsic_kwargs = True + + main([filename, "--enable-sign-intrinsic-argnames"]) + assert Config.get().sign_intrinsic_kwargs is True + + # Test we get the expected log message with both options + caplog.clear() + with caplog.at_level(logging.INFO, "psyclone.generator"): + main([filename, "--disable-intrinsic-required-args", + "--enable-sign-intrinsic-argnames"]) + assert caplog.records[0].levelname == "INFO" + assert ("Both the disable-intrinsic-required-args and the " + "enable-sign-intrinsic-argnames were specified. " + "The disable-intrinsic-required-args overrides other " + "controls so sign intrinsics won't have output argument " + "names." in caplog.text) diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 865b59c0be..4135bb33b8 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -42,6 +42,7 @@ from collections import OrderedDict import pytest +from psyclone.configuration import Config from psyclone.psyir.backend.visitor import VisitorError from psyclone.psyir.backend.fortran import ( gen_intent, FortranWriter, precedence @@ -1843,6 +1844,43 @@ def test_fw_call_node(fortran_writer): assert expected in result +def test_fw_intrinsic_output_control(fortran_writer): + '''Test the config controls the output of IntrinsicCall nodes correctly. + ''' + args = [Reference(DataSymbol("arg1", REAL_TYPE)), + Reference(DataSymbol("arg2", REAL_TYPE))] + call = IntrinsicCall.create(IntrinsicCall.Intrinsic.ISHFT, + args) + result = fortran_writer(call) + assert "ISHFT(i=arg1, shift=arg2)" in result + + # Turn off the output of required arguments + Config.get().intrinsic_kwargs = False + result = fortran_writer(call) + assert "ISHFT(arg1, arg2)" in result + + # Check that optional arguments are still output. + args = [Reference(DataSymbol("arg1", REAL_TYPE)), + Reference(DataSymbol("arg2", REAL_TYPE)), + Reference(DataSymbol("arg3", REAL_TYPE))] + call = IntrinsicCall.create(IntrinsicCall.Intrinsic.ISHFTC, + args) + result = fortran_writer(call) + assert "ISHFTC(arg1, arg2, size=arg3)" in result + + # Test the sign only option control. + Config.get().intrinsic_kwargs = True + args = [Reference(DataSymbol("arg1", REAL_TYPE)), + Reference(DataSymbol("arg2", REAL_TYPE))] + call = IntrinsicCall.create(IntrinsicCall.Intrinsic.SIGN, args) + result = fortran_writer(call) + assert "SIGN(arg1, arg2)" in result + + Config.get().sign_intrinsic_kwargs = True + result = fortran_writer(call) + assert "SIGN(a=arg1, b=arg2)" in result + + def test_fw_call_node_namedargs(fortran_writer): '''Test the PSyIR call node is translated to the required Fortran code when there are named arguments and that the expected exception is diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index af76bdefcb..478450ce61 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -193,7 +193,7 @@ def test_intrinsic_handler_intrinsiccall_onearg( ('x = min(a, b, c)', IntrinsicCall.Intrinsic.MIN, [None, None, None]), ('x = sign(a, b)', IntrinsicCall.Intrinsic.SIGN, - [None, None]), + ["a", "b"]), ('x = sqrt(a)', IntrinsicCall.Intrinsic.SQRT, ["x"]), ('x = aimag(a)', IntrinsicCall.Intrinsic.AIMAG, diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index cceedeb32d..567fcde159 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -768,10 +768,30 @@ def test_canonicalisation(): intrinsic.canonicalise() assert intrinsic.argument_names == ["array", "dim", "mask"] - print("-------------") # Check that we don't fail when the required argument name is None # and no argument name can be generated by PSyclone. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.canonicalise() assert intrinsic.argument_names == [None] + + +def test_remove_required_argument_names(): + '''Test the remove required argument names works correctly.''' + # Check that optional names are added and the only ones kept. + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) + intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) + intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) + intrinsic.remove_required_argument_names() + assert intrinsic.argument_names == [None, None, "mask"] + + # Check nothing happens if the intrinsic can't be canonicalised + intrinsic = IntrinsicCall.create( + IntrinsicCall.Intrinsic.MAX, + [("a1", Reference(DataSymbol("a", INTEGER_TYPE))), + ("a2", Reference(DataSymbol("b", INTEGER_TYPE))), + ("a3", Reference(DataSymbol("c", INTEGER_TYPE)))] + ) + intrinsic.remove_required_argument_names() + assert intrinsic.argument_names == ["a1", "a2", "a3"] From ab0225eb4e218f16c5e3ca2e2a491c6c2ef4a731 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 8 Oct 2025 15:17:14 +0100 Subject: [PATCH 27/50] Added doc updates --- doc/user_guide/psyclone_command.rst | 14 ++++++++++++++ src/psyclone/generator.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/user_guide/psyclone_command.rst b/doc/user_guide/psyclone_command.rst index 314c0e13e5..abce5d5d03 100644 --- a/doc/user_guide/psyclone_command.rst +++ b/doc/user_guide/psyclone_command.rst @@ -60,6 +60,7 @@ by the command: [--log-level {OFF,DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-file LOG_FILE] [--keep-comments] [--keep-directives] [-I INCLUDE] [-d DIRECTORY] [--modman-file-ignore IGNORE_PATTERN] [--free-form | --fixed-form] + [--disable-intrinsic-required-args] [--enable-sign-intrinsic-argnames] filename Transform a file using the PSyclone source-to-source Fortran compiler @@ -123,6 +124,19 @@ by the command: --modman-file-ignore IGNORE_PATTERN Ignore files that contain the specified pattern. + Fortran Intrinsic output control: + These settings control how PSyclone outputs Fortran Intrinsics. The default behaviour + is to output named arguments for all intrinsics' arguments other than SIGN, + which will not have keyword arguments to support NEMO behaviour. + + --disable-intrinsic-required-args + Disables output code containing argument names for an intrinsic's + required arguments, i.e. SUM(arr, mask=maskarr) instead of + SUM(array=arr, mask=maskarr). This overrides any other options specified + for intrinsic output control. + --enable-sign-intrinsic-argnames + Enables adding argument names to the SIGN intrinsic's arguments. + Basic Use --------- diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index ec744e7fa7..b7a8c788bb 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -586,7 +586,7 @@ def main(arguments): action="store_true", help="Disables output code containing argument names for an " "intrinsic's required arguments, i.e. SUM(arr, mask=maskarr) " - "instead of (SUMarray=arr, mask=maskarr). This overrides any " + "instead of SUM(array=arr, mask=maskarr). This overrides any " "other options specified for intrinsic output control." ) intrinsic_output_group.add_argument( From c04f77adb198b5ab755a495cd3b1201eb4ad9e65 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 17 Oct 2025 15:29:56 +0100 Subject: [PATCH 28/50] First set of fixes towards review --- src/psyclone/configuration.py | 34 +-- src/psyclone/generator.py | 20 +- src/psyclone/psyir/backend/fortran.py | 77 +++++-- src/psyclone/psyir/backend/sympy_writer.py | 2 +- src/psyclone/psyir/backend/visitor.py | 2 - src/psyclone/psyir/nodes/call.py | 8 +- src/psyclone/psyir/nodes/intrinsic_call.py | 216 +++++++----------- src/psyclone/tests/configuration_test.py | 20 +- .../tests/domain/lfric/lfric_builtins_test.py | 4 +- src/psyclone/tests/generator_test.py | 20 +- .../tests/psyir/backend/fortran_test.py | 15 +- src/psyclone/tests/psyir/nodes/call_test.py | 4 +- .../tests/psyir/nodes/intrinsic_call_test.py | 71 ++---- .../intrinsics/sign2code_trans_test.py | 10 +- 14 files changed, 207 insertions(+), 296 deletions(-) diff --git a/src/psyclone/configuration.py b/src/psyclone/configuration.py index 420574f859..d1e02e6f09 100644 --- a/src/psyclone/configuration.py +++ b/src/psyclone/configuration.py @@ -237,8 +237,7 @@ def __init__(self): # By default, the PSyIR backends output argument names on (most) # IntrinsicCalls. These two options enable control of tha behaviour. - self._sign_intrinsic_kwargs = False - self._intrinsic_kwargs = True + self._backend_intrinsic_named_kwargs = True # ------------------------------------------------------------------------- def load(self, config_file=None): @@ -777,41 +776,26 @@ def get_constants(self): return self.api_conf().get_constants() @property - def intrinsic_kwargs(self) -> bool: + def backend_intrinsic_named_kwargs(self) -> bool: ''' :returns: whether the output of intrinsic named arguments is enabled for required intrinsic arguments. ''' - return self._intrinsic_kwargs + return self._backend_intrinsic_named_kwargs - @intrinsic_kwargs.setter - def intrinsic_kwargs(self, output_kwargs: bool) -> None: + @backend_intrinsic_named_kwargs.setter + def backend_intrinsic_named_kwargs(self, output_kwargs: bool) -> None: ''' Setter for whether the backend should output required argument names on IntrinsicCalls. :param output_kwargs: whether to output required argument names. ''' - self._intrinsic_kwargs = output_kwargs + if not isinstance(output_kwargs, bool): + raise TypeError(f"backend_intrinsic_named_kwargs must be a bool " + f"but found '{type(output_kwargs).__name__}'.") - @property - def sign_intrinsic_kwargs(self) -> bool: - ''' - :returns: whether the output of the sign intrinsic should have - named arguments. - ''' - return self._sign_intrinsic_kwargs - - @sign_intrinsic_kwargs.setter - def sign_intrinsic_kwargs(self, output_kwargs: bool) -> None: - ''' - Setter for whether the backend should output argument names on the - SIGN IntrinsicCall. - - :param output_kwargs: whether to output argument names on SIGN - intrinsics. - ''' - self._sign_intrinsic_kwargs = output_kwargs + self._backend_intrinsic_named_kwargs = output_kwargs # ============================================================================= diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index b7a8c788bb..4dd4ed8d29 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -586,14 +586,7 @@ def main(arguments): action="store_true", help="Disables output code containing argument names for an " "intrinsic's required arguments, i.e. SUM(arr, mask=maskarr) " - "instead of SUM(array=arr, mask=maskarr). This overrides any " - "other options specified for intrinsic output control." - ) - intrinsic_output_group.add_argument( - "--enable-sign-intrinsic-argnames", default=argparse.SUPPRESS, - action="store_true", - help="Enables adding argument names to the SIGN intrinsic's " - "arguments." + "instead of SUM(array=arr, mask=maskarr)." ) args = parser.parse_args(arguments) @@ -650,16 +643,7 @@ def main(arguments): # Record any intrinsic output format settings. if "disable_intrinsic_required_args" in args: - Config.get().intrinsic_kwargs = False - if "enable_sign_intrinsic_argnames" in args: - if not Config.get().intrinsic_kwargs: - logger.info( - "Both the disable-intrinsic-required-args and the " - "enable-sign-intrinsic-argnames were specified. The " - "disable-intrinsic-required-args overrides other controls " - "so sign intrinsics won't have output argument names." - ) - Config.get().sign_intrinsic_kwargs = True + Config.get().backend_intrinsic_named_kwargs = False # Record any profiling options. if args.profile: diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 8b4ecd9643..b433b58982 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1759,27 +1759,52 @@ def _gen_arguments(self, node): result_list.append(self._visit(child)) return ", ".join(result_list) - def call_node(self, node) -> str: - '''Translate the PSyIR call node to Fortran. - - :param node: a Call PSyIR node. - :type node: :py:class:`psyclone.psyir.nodes.Call` + def intrinsiccall_node(self, node: IntrinsicCall) -> str: + '''Translate the PSyIR IntrinsicCall node to Fortran. + :param node: an IntrinsicCall PSyIR node. + :returns: the equivalent Fortran code. ''' - # If its an IntrinsicCall then check the config to determine how - # we're outputting the result. - if isinstance(node, IntrinsicCall): - if not Config.get().intrinsic_kwargs: - # Remove argument names from required arguments. - node.remove_required_argument_names() - elif (node.intrinsic == IntrinsicCall.Intrinsic.SIGN and - not Config.get().sign_intrinsic_kwargs): - # Remove argument names from Sign. - node.remove_required_argument_names() - args = self._gen_arguments(node) - if isinstance(node, IntrinsicCall) and node.routine.name not in [ + # Check the config to determine if we're outputting all argument + # names. + if not Config.get().backend_intrinsic_named_kwargs: + # Config says to avoid outputting argument names where + # possible. + try: + # Canonicalisation handles any error checking we might + # otherwise want to try. Most IntrinsicCalls should already + # be canonicalised, but we do it here to ensure that it is + # is possible. + node.canonicalise() + intrinsic_interface = node._find_matching_interface() + args = [] + correct_names = True + for idx, arg_name in enumerate(node.argument_names): + if idx < len(intrinsic_interface) and correct_names: + # This is a potential required argument. + if arg_name == intrinsic_interface[idx]: + args.append(self._visit(node.arguments[idx])) + continue + # Otherwise it didn't match, so we can't remove any + # more argument names, and fall back to the default + # behaviour from here. + correct_names = False + # Otherwise, use the default behaviour. + args.append( + f"{node.argument_names[idx]}=" + f"{self._visit(node.arguments[idx])}" + ) + args = ", ".join(args) + except NotImplementedError: + # If the Intrinsic fails to canonicalise, or to match + # to an interface, then use the default behaviour. + args = self._gen_arguments(node) + else: + args = self._gen_arguments(node) + + if node.routine.name not in [ "DATE_AND_TIME", "SYSTEM_CLOCK", "MVBITS", "RANDOM_NUMBER", "RANDOM_SEED"]: # Most intrinsics are functions and so don't have 'call'. @@ -1793,6 +1818,24 @@ def call_node(self, node) -> str: # Otherwise it is inside-expression function call return f"{self._visit(node.routine)}({args})" + + def call_node(self, node) -> str: + '''Translate the PSyIR call node to Fortran. + + :param node: a Call PSyIR node. + :type node: :py:class:`psyclone.psyir.nodes.Call` + + :returns: the equivalent Fortran code. + + ''' + args = self._gen_arguments(node) + + if not node.parent or isinstance(node.parent, Schedule): + return f"{self._nindent}call {self._visit(node.routine)}({args})\n" + + # Otherwise it is inside-expression function call + return f"{self._visit(node.routine)}({args})" + def kernelfunctor_node(self, node): ''' Translate the Kernel functor into Fortran. diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index 958e8e7bb7..67a196148c 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -786,7 +786,7 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: args = self._gen_arguments(node) return f"{self._nindent}{name}({args})" except KeyError: - return super().call_node(node) + return super().intrinsiccall_node(node) # ------------------------------------------------------------------------- def reference_node(self, node: Reference) -> str: diff --git a/src/psyclone/psyir/backend/visitor.py b/src/psyclone/psyir/backend/visitor.py index a577b9bd04..af602e8615 100644 --- a/src/psyclone/psyir/backend/visitor.py +++ b/src/psyclone/psyir/backend/visitor.py @@ -262,7 +262,6 @@ def _visit(self, node): # Check global constraints for this node (if validation enabled). if self._validate_nodes: node.validate_global_constraints() - # Make a list of the node's ancestor classes (including # itself) in method resolution order (mro), apart from the # base "object" class. @@ -275,7 +274,6 @@ def _visit(self, node): for method_name in possible_method_names: try: node_result = getattr(self, method_name)(node) - # We can only proceed to add comments if the Visitor # returned a string, otherwise we just return if not isinstance(node_result, str): diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py index a6d7580be7..9ad40fca2c 100644 --- a/src/psyclone/psyir/nodes/call.py +++ b/src/psyclone/psyir/nodes/call.py @@ -392,10 +392,12 @@ def argument_by_name(self, name: str) -> Union[DataNode, None]: :returns: The argument specified with the input name, or None if its not present. ''' - arg_names = self.argument_names - if name not in arg_names: + arg_names = [arg.lower() if arg is not None else None + for arg in self.argument_names] + lname = name.lower() + if lname not in arg_names: return None - return self.arguments[arg_names.index(name)] + return self.arguments[arg_names.index(lname)] @property def is_elemental(self): diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 7df3ffd5b3..f81cc5c22a 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3179,109 +3179,87 @@ def _find_matching_interface(self) -> Tuple[str]: interface that matches this IntrinsicCall. ''' - # Pull out the list of optional argument names. - optional_names = list(self.intrinsic.optional_args.keys()) - # Create a list of all the possible interface's argument lists. - potential_interfaces: List[Tuple[str]] = [ - names for names in self.intrinsic.required_args.arg_names - ] - # Remove any of the interfaces that don't contain - # a named non-optional argument from the list of potential - # candidate interfaces. - for name in self.argument_names: - if not name: - continue - # Optional argument names are skipped over as they don't - # affect which interface is being used. - if name in optional_names: - continue - for arglist in potential_interfaces: - if name not in arglist: - potential_interfaces.remove(arglist) - - # Remove any of the interfaces that have too many or - # too few *total* arguments to be candidates. - for choice in potential_interfaces[:]: - min_args = len(choice) - max_args = min_args + len(optional_names) - if (len(self.arguments) < min_args or - len(self.arguments) > max_args): - potential_interfaces.remove(choice) - - # Remove any of the interfaces that have too many or - # too few *required* arguments to be candidates. - # At this point the total arguments must be valid for all - # remaining choices, and all named arguments must also be - # present. - for choice in potential_interfaces[:]: - required_args = len(choice) - # Check if the number of unnamed arguments is greater - # than the number of required arguments. If so then - # this choice is still acceptable (because optional - # arguments can also be positional). - num_positional_arguments = len( - [x for x in self.argument_names if x is None] - ) - if num_positional_arguments >= required_args: - continue - # Otherwise we need to check if all the - # required arguments are present as named arguments. - # This operation pulls all the argument names from the - # potential interface that are not already matched to a - # positional argument in this IntrinsicCall. These must - # be matched to named arguments in this IntrinsicCall, else - # this interface cannot be a candidate for canonicalisation. - remaining_required = choice[num_positional_arguments:] - for name in remaining_required: - if name not in self.argument_names: + if len(self.intrinsic.required_args.arg_names) > 1: + # Pull out the list of optional argument names. + optional_names = list(self.intrinsic.optional_args.keys()) + # Create a list of all the possible interface's argument lists. + potential_interfaces: List[Tuple[str]] = [ + names for names in self.intrinsic.required_args.arg_names + ] + # Remove any of the interfaces that don't contain + # a named non-optional argument from the list of potential + # candidate interfaces. + for name in self.argument_names: + if not name: + continue + # Optional argument names are skipped over as they don't + # affect which interface is being used. + if name in optional_names: + continue + for arglist in potential_interfaces: + if name not in arglist: + potential_interfaces.remove(arglist) + + # Remove any of the interfaces that have too many or + # too few *total* arguments to be candidates. + for choice in potential_interfaces[:]: + min_args = len(choice) + max_args = min_args + len(optional_names) + if (len(self.arguments) < min_args or + len(self.arguments) > max_args): potential_interfaces.remove(choice) - break - - # If we didn't reduce the number of potential interfacfes to a - # single interface then we can't canonicalise. - if (len(potential_interfaces) > 1 or - len(potential_interfaces) == 0): - raise NotImplementedError( - f"Cannot canonicalise '{self.intrinsic.name}' " - f"IntrinsicCall as PSyclone can't determine which " - f"argument set it should use. This can be resolved by " - f"using named arguments in the Fortran source." - ) - return potential_interfaces[0] - - def remove_required_argument_names(self) -> None: - '''Remove the argument names from required arguments on this - IntrinsicCall if possible.''' - # First we need to ensure this is a canoncalised IntrinsicCall - try: - self.canonicalise() - except (ValueError, NotImplementedError): - # If this can't be canonicalised then don't change anything. - return - # Find all of the required argument names available on this - # intrinsic. - all_required_names = [ - name for tupl in self.intrinsic.required_args.arg_names for - name in tupl - ] - new_arg_names = [] - for i, name in enumerate(self.argument_names): - if name in all_required_names: - new_arg_names.append( - (self._argument_names[i][0], None) + # Remove any of the interfaces that have too many or + # too few *required* arguments to be candidates. + # At this point the total arguments must be valid for all + # remaining choices, and all named arguments must also be + # present. + for choice in potential_interfaces[:]: + required_args = len(choice) + # Check if the number of unnamed arguments is greater + # than the number of required arguments. If so then + # this choice is still acceptable (because optional + # arguments can also be positional). + num_positional_arguments = len( + [x for x in self.argument_names if x is None] ) - else: - new_arg_names.append( - (self._argument_names[i][0], name) + if num_positional_arguments >= required_args: + continue + # Otherwise we need to check if all the + # required arguments are present as named arguments. + # This operation pulls all the argument names from the + # potential interface that are not already matched to a + # positional argument in this IntrinsicCall. These must + # be matched to named arguments in this IntrinsicCall, else + # this interface cannot be a candidate for canonicalisation. + remaining_required = choice[num_positional_arguments:] + for name in remaining_required: + if name not in self.argument_names: + potential_interfaces.remove(choice) + break + + # If we didn't reduce the number of potential interfacfes to a + # single interface then we can't canonicalise. + if (len(potential_interfaces) > 1 or + len(potential_interfaces) == 0): + raise NotImplementedError( + f"Cannot canonicalise '{self.intrinsic.name}' " + f"IntrinsicCall as PSyclone can't determine which " + f"argument set it should use. This can be resolved by " + f"using named arguments in the Fortran source." ) - self._argument_names = new_arg_names + return potential_interfaces[0] + elif len(self.intrinsic.required_args.arg_names) == 1: + # This intrinsic only has a single possible interface. + return self.intrinsic.required_args.arg_names[0] + else: + # This intrinsic has no required arguments. + return () + def canonicalise(self): '''Canonicalise an IntrinsicCall in the PSyIR. Upon successful - canonicalisation, all arguments will become named arguments and - arguments may be reordered to match what is defined in the standard - for the argument ordering. + canonicalisation, all arguments will become named arguments. A small number of intrinsics (e.g. ALLOCATE) never have ambiguity and no argument limits, in which case no canonicalisation is done. @@ -3354,14 +3332,7 @@ def canonicalise(self): ) # Find which intrinsic call interface we are canonicalising with. - if len(self.intrinsic.required_args.arg_names) > 1: - interface_arg_names = self._find_matching_interface() - elif len(self.intrinsic.required_args.arg_names) == 1: - # This intrinsic only has a single possible interface. - interface_arg_names = self.intrinsic.required_args.arg_names[0] - else: - # This intrinsic has no required arguments. - interface_arg_names = () + interface_arg_names = self._find_matching_interface() # Handle cases where None or "" is in the interface_arg_names, # as this implies context sensitive argument naming which PSyclone @@ -3403,8 +3374,13 @@ def canonicalise(self): # We found a required argument without a name. # Update the argument_names tuple with the corresponding # name from the matched interface. - self._argument_names[i] = (self._argument_names[i][0], - interface_arg_names[i]) + # If the argument name is '' then it needs to be None. + if interface_arg_names[i]: + self._argument_names[i] = (self._argument_names[i][0], + interface_arg_names[i]) + else: + self._argument_names[i] = (self._argument_names[i][0], + None) continue # Otherwise we found an optional argument, which will always # be in order if unnamed. @@ -3412,34 +3388,6 @@ def canonicalise(self): optional_names[i - len( interface_arg_names)]) - # We have all arguments named now, we want to reorder them. - new_arg_names = [] - new_args = [] - - for required in interface_arg_names: - # Skip over required argument names when they're either None or '' - # as these designate arguments PSyclone cannoot canonicalise to - # names. - if not required: - continue - index = self.argument_names.index(required) - new_arg_names.append(self._argument_names[index]) - new_args.append(self.arguments[index]) - - for option in optional_names: - if option not in self.argument_names: - continue - index = self.argument_names.index(option) - new_arg_names.append(self._argument_names[index]) - new_args.append(self.arguments[index]) - - # Replace the argument list with the canonicalised version. - if len(new_args) > 0: - for child in self.arguments: - child.detach() - for child in new_args: - self.addchild(child) - self._argument_names = new_arg_names @classmethod def create(cls, intrinsic, arguments=()): diff --git a/src/psyclone/tests/configuration_test.py b/src/psyclone/tests/configuration_test.py index cea567ec09..efaee81b49 100644 --- a/src/psyclone/tests/configuration_test.py +++ b/src/psyclone/tests/configuration_test.py @@ -822,12 +822,14 @@ def test_fortran_standard(tmpdir): def test_intrinsic_settings(): - '''Test the getter and setter methods for intrinsic output control - in the config.''' - assert Config.get().intrinsic_kwargs is True - Config.get().intrinsic_kwargs = False - assert Config.get().intrinsic_kwargs is False - - assert Config.get().sign_intrinsic_kwargs is False - Config.get().sign_intrinsic_kwargs = True - assert Config.get().sign_intrinsic_kwargs is True + '''Test the getter and setter methods for controlling the output of + named arguments on intrinsics in the config.''' + assert Config.get().backend_intrinsic_named_kwargs is True + Config.get().backend_intrinsic_named_kwargs = False + assert Config.get().backend_intrinsic_named_kwargs is False + + with pytest.raises(TypeError) as err: + Config.get().backend_intrinsic_named_kwargs = 1 + + assert ("backend_intrinsic_named_kwargs must be a bool but found " + "'int'." in str(err.value)) diff --git a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py index 68b91d2e88..99a83eb327 100644 --- a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py @@ -1654,7 +1654,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(a, f1_data(df))\n") in code + "f2_data(df) = SIGN(a=a, b=f1_data(df))\n") in code # Also with a literal kern = builtin_from_file("15.10.2_sign_X_builtin_set_by_value.f90") @@ -1665,7 +1665,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(-2.0_r_def, f1_data(df))\n" in code) + "f2_data(df) = SIGN(a=-2.0_r_def, b=f1_data(df))\n" in code) # The integer version has the datatype changed to integer in the metadata # and string representation diff --git a/src/psyclone/tests/generator_test.py b/src/psyclone/tests/generator_test.py index 980ac00ab5..8530bffb72 100644 --- a/src/psyclone/tests/generator_test.py +++ b/src/psyclone/tests/generator_test.py @@ -2045,22 +2045,4 @@ def test_intrinsic_control_settings(tmpdir, caplog): with open(filename, "w", encoding='utf-8') as my_file: my_file.write(code) main([filename, "--disable-intrinsic-required-args"]) - assert Config.get().intrinsic_kwargs is False - - # Reset the config - Config.get().intrinsic_kwargs = True - - main([filename, "--enable-sign-intrinsic-argnames"]) - assert Config.get().sign_intrinsic_kwargs is True - - # Test we get the expected log message with both options - caplog.clear() - with caplog.at_level(logging.INFO, "psyclone.generator"): - main([filename, "--disable-intrinsic-required-args", - "--enable-sign-intrinsic-argnames"]) - assert caplog.records[0].levelname == "INFO" - assert ("Both the disable-intrinsic-required-args and the " - "enable-sign-intrinsic-argnames were specified. " - "The disable-intrinsic-required-args overrides other " - "controls so sign intrinsics won't have output argument " - "names." in caplog.text) + assert Config.get().backend_intrinsic_named_kwargs is False diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 4135bb33b8..5e869c05e2 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -1852,10 +1852,11 @@ def test_fw_intrinsic_output_control(fortran_writer): call = IntrinsicCall.create(IntrinsicCall.Intrinsic.ISHFT, args) result = fortran_writer(call) + # Default behaviour is to include argument names. assert "ISHFT(i=arg1, shift=arg2)" in result # Turn off the output of required arguments - Config.get().intrinsic_kwargs = False + Config.get().backend_intrinsic_named_kwargs = False result = fortran_writer(call) assert "ISHFT(arg1, arg2)" in result @@ -1868,18 +1869,6 @@ def test_fw_intrinsic_output_control(fortran_writer): result = fortran_writer(call) assert "ISHFTC(arg1, arg2, size=arg3)" in result - # Test the sign only option control. - Config.get().intrinsic_kwargs = True - args = [Reference(DataSymbol("arg1", REAL_TYPE)), - Reference(DataSymbol("arg2", REAL_TYPE))] - call = IntrinsicCall.create(IntrinsicCall.Intrinsic.SIGN, args) - result = fortran_writer(call) - assert "SIGN(arg1, arg2)" in result - - Config.get().sign_intrinsic_kwargs = True - result = fortran_writer(call) - assert "SIGN(a=arg1, b=arg2)" in result - def test_fw_call_node_namedargs(fortran_writer): '''Test the PSyIR call node is translated to the required Fortran code diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py index f7740fa655..4292e5d9b4 100644 --- a/src/psyclone/tests/psyir/nodes/call_test.py +++ b/src/psyclone/tests/psyir/nodes/call_test.py @@ -373,10 +373,12 @@ def test_call_argument_by_name(): op1 = Literal("1", INTEGER_TYPE) op2 = Literal("2", INTEGER_TYPE) op3 = Literal("3", INTEGER_TYPE) - call = Call.create(RoutineSymbol("hello"), [op1, ("a", op2), ("b", op3)]) + call = Call.create(RoutineSymbol("hello"), [op1, ("A", op2), ("b", op3)]) assert call.argument_by_name("z") is None assert call.argument_by_name("a") is op2 + assert call.argument_by_name("A") is op2 assert call.argument_by_name("b") is op3 + assert call.argument_by_name("B") is op3 def test_call_reference_accesses(): diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index 567fcde159..85dafa3c8b 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -293,18 +293,18 @@ def test_intrinsiccall_minmaxsum_create(intrinsic_call): intrinsic = IntrinsicCall.create( intrinsic_call, [Reference(array), ("mask", Reference(mask)), ("dim", Reference(dim))]) - assert intrinsic.argument_names == ["array", "dim", "mask"] - assert intrinsic.children[2].symbol.name == "dim" - assert intrinsic.children[3].symbol.name == "mask" + assert intrinsic.argument_names == ["array", "mask", "dim"] + assert intrinsic.children[2].symbol.name == "mask" + assert intrinsic.children[3].symbol.name == "dim" # array and optional literal mask and optional literal dim intrinsic = IntrinsicCall.create( intrinsic_call, [ Reference(array), ("mask", Literal("1", INTEGER_TYPE)), ("dim", Literal("false", BOOLEAN_TYPE))]) - assert intrinsic.argument_names == ["array", "dim", "mask"] - assert intrinsic.children[2].value == "false" - assert intrinsic.children[3].value == "1" + assert intrinsic.argument_names == ["array", "mask", "dim"] + assert intrinsic.children[2].value == "1" + assert intrinsic.children[3].value == "false" @pytest.mark.parametrize("intrinsic_call", [ @@ -440,9 +440,9 @@ def test_create_positional_arguments_with_names(): [("vector_b", bref.copy()), ("vector_a", aref.copy())]) assert isinstance(intr, IntrinsicCall) - assert intr.arguments[0] == aref - assert intr.arguments[1] == bref - assert intr.argument_names == ["vector_a", "vector_b"] + assert intr.arguments[0] == bref + assert intr.arguments[1] == aref + assert intr.argument_names == ["vector_b", "vector_a"] @pytest.mark.parametrize("operator", ["lbound", "ubound", "size"]) @@ -582,7 +582,7 @@ def test_verify_intrinsic(fortran_reader, fortran_writer): assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " "kind=kind(x=1)) == 0) then" in result) assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " - "back=.true., kind=kind(x=1)) == 0) then" in result) + "kind=kind(x=1), back=.true.) == 0) then" in result) def test_intrinsic_canonicalisation_value_errors(): @@ -595,7 +595,7 @@ def test_intrinsic_canonicalisation_value_errors(): intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) # Set up the argument_names array - intrinsic.argument_names + _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "wrong") with pytest.raises(ValueError) as err: intrinsic.canonicalise() @@ -626,7 +626,7 @@ def test_intrinsic_canonicalisation_value_errors(): def test_intrinsic_canonicalisation_not_implemented_errors(): ''' Test the canonicalisation function of the IntrinsicCall class raises - ValueErrors for Intrinsic structures PSyclone can't handle. + NotImplementedErrors for Intrinsic structures PSyclone can't handle. ''' # Test canonicalisation doesn't work when we have 2 arguments for SUM # with no naming, as it can't determine between the SUM variants. @@ -648,7 +648,7 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) # Set up the argument_names array and set the argument names - intrinsic.argument_names + _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "n1") intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "n2") with pytest.raises(NotImplementedError) as err: @@ -663,7 +663,7 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): # names. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.argument_names + _= intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "array") with pytest.raises(NotImplementedError) as err: intrinsic.canonicalise() @@ -689,7 +689,7 @@ def test_canonicalisation(): intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) # Set up the argument_names array and set the second ones name to be mask - intrinsic.argument_names + _ = intrinsic.argument_names intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "mask") intrinsic.canonicalise() assert intrinsic.argument_names == ["array", "mask"] @@ -700,7 +700,7 @@ def test_canonicalisation(): intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) # Set up the argument_names array and set the second ones name to be dim - intrinsic.argument_names + _ = intrinsic.argument_names intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "dim") intrinsic.canonicalise() assert intrinsic.argument_names[0] == "array" @@ -715,15 +715,13 @@ def test_canonicalisation(): intrinsic.addchild(b_arg) # Set up the argument_names array and set the first ones name to be mask # (optional) and the second to be array. - intrinsic.argument_names + _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "mask") intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "array") intrinsic.canonicalise() - # Canonicalisation should have the names as array then mask, and have - # reversed the argument order to match the movement of the argument names. - assert intrinsic.argument_names == ["array", "mask"] - assert intrinsic.arguments[0] is b_arg - assert intrinsic.arguments[1] is a_arg + assert intrinsic.argument_names == ["mask", "array"] + assert intrinsic.arguments[0] is a_arg + assert intrinsic.arguments[1] is b_arg # Check we can canonicalise an intrinsic with only one argument set. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SIN) @@ -744,10 +742,10 @@ def test_canonicalisation(): "count_max") intrinsic._argument_names[2] = (intrinsic._argument_names[2][0], "count") intrinsic.canonicalise() - assert intrinsic.argument_names == ["count", "count_rate", "count_max"] - assert intrinsic.children[1].symbol.name == "c" - assert intrinsic.children[2].symbol.name == "a" - assert intrinsic.children[3].symbol.name == "b" + assert intrinsic.argument_names == ["count_rate", "count_max", "count"] + assert intrinsic.children[1].symbol.name == "a" + assert intrinsic.children[2].symbol.name == "b" + assert intrinsic.children[3].symbol.name == "c" # Test canonicliation for intrinsic when PSyclone can't # canonicalise the names of non-optional arguments. @@ -774,24 +772,3 @@ def test_canonicalisation(): intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.canonicalise() assert intrinsic.argument_names == [None] - - -def test_remove_required_argument_names(): - '''Test the remove required argument names works correctly.''' - # Check that optional names are added and the only ones kept. - intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) - intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) - intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) - intrinsic.remove_required_argument_names() - assert intrinsic.argument_names == [None, None, "mask"] - - # Check nothing happens if the intrinsic can't be canonicalised - intrinsic = IntrinsicCall.create( - IntrinsicCall.Intrinsic.MAX, - [("a1", Reference(DataSymbol("a", INTEGER_TYPE))), - ("a2", Reference(DataSymbol("b", INTEGER_TYPE))), - ("a3", Reference(DataSymbol("c", INTEGER_TYPE)))] - ) - intrinsic.remove_required_argument_names() - assert intrinsic.argument_names == ["a1", "a2", "a3"] diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py index 3e411ca783..f10d07a576 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py @@ -110,7 +110,7 @@ def test_correct(func, output, tmpdir): f" real, intent(inout) :: arg\n" f" real, intent(inout) :: arg_1\n" f" real :: psyir_tmp\n\n" - f" psyir_tmp = SIGN({output}, arg_1)\n\n" + f" psyir_tmp = SIGN(a={output}, b=arg_1)\n\n" f"end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call) @@ -164,7 +164,7 @@ def test_correct_expr(tmpdir): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = 1.0 + SIGN(arg * 3.14, arg_1) + 2.0\n\n" + " psyir_tmp = 1.0 + SIGN(a=arg * 3.14, b=arg_1) + 2.0\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call) @@ -218,7 +218,7 @@ def test_correct_2sign(tmpdir, fortran_writer): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = SIGN(1.0, 1.0) + SIGN(arg * 3.14, arg_1)\n\n" + " psyir_tmp = SIGN(a=1.0, b=1.0) + SIGN(a=arg * 3.14, b=arg_1)\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call) @@ -311,8 +311,8 @@ def test_sign_of_unknown_type(fortran_reader): if call.intrinsic.name == "SIGN"] with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[0]) - assert ("Sign2CodeTrans cannot be applied to 'SIGN(MAX(ABS(a=ztmp1), " - "1.e-6_wp), ztmp1) because the type of the argument" + assert ("Sign2CodeTrans cannot be applied to 'SIGN(a=MAX(ABS(a=ztmp1), " + "1.e-6_wp), b=ztmp1) because the type of the argument" in str(err.value)) with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[1]) From 30af84f56cefc9f8ad4cca74cbe44c84f3d7324a Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 17 Oct 2025 15:31:05 +0100 Subject: [PATCH 29/50] linting issues --- src/psyclone/psyir/backend/fortran.py | 5 ++--- src/psyclone/psyir/nodes/intrinsic_call.py | 2 -- src/psyclone/tests/psyir/nodes/intrinsic_call_test.py | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index b433b58982..a17d3b115c 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1763,7 +1763,7 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: '''Translate the PSyIR IntrinsicCall node to Fortran. :param node: an IntrinsicCall PSyIR node. - + :returns: the equivalent Fortran code. ''' @@ -1791,7 +1791,7 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: # more argument names, and fall back to the default # behaviour from here. correct_names = False - # Otherwise, use the default behaviour. + # Otherwise, use the default behaviour. args.append( f"{node.argument_names[idx]}=" f"{self._visit(node.arguments[idx])}" @@ -1818,7 +1818,6 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: # Otherwise it is inside-expression function call return f"{self._visit(node.routine)}({args})" - def call_node(self, node) -> str: '''Translate the PSyIR call node to Fortran. diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index f81cc5c22a..386bf58e00 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3256,7 +3256,6 @@ def _find_matching_interface(self) -> Tuple[str]: # This intrinsic has no required arguments. return () - def canonicalise(self): '''Canonicalise an IntrinsicCall in the PSyIR. Upon successful canonicalisation, all arguments will become named arguments. @@ -3388,7 +3387,6 @@ def canonicalise(self): optional_names[i - len( interface_arg_names)]) - @classmethod def create(cls, intrinsic, arguments=()): '''Create an instance of this class given the type of intrinsic and a diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index 85dafa3c8b..eeff797014 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -663,7 +663,7 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): # names. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - _= intrinsic.argument_names + _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "array") with pytest.raises(NotImplementedError) as err: intrinsic.canonicalise() From 9fff0bdd0bad62226ae716bc441abe91b40d8914 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Mon, 20 Oct 2025 11:05:31 +0100 Subject: [PATCH 30/50] Changes for review --- doc/user_guide/psyclone_command.rst | 33 +++++------- src/psyclone/generator.py | 25 ++++----- src/psyclone/psyir/nodes/intrinsic_call.py | 2 +- .../tests/psyir/backend/fortran_test.py | 53 +++++++++++++++++++ .../tests/psyir/nodes/intrinsic_call_test.py | 2 +- 5 files changed, 79 insertions(+), 36 deletions(-) diff --git a/doc/user_guide/psyclone_command.rst b/doc/user_guide/psyclone_command.rst index abce5d5d03..bd7843addf 100644 --- a/doc/user_guide/psyclone_command.rst +++ b/doc/user_guide/psyclone_command.rst @@ -50,17 +50,17 @@ by the command: .. parsed-literal:: + > psyclone -h usage: psyclone [-h] [-v] [-c CONFIG] [-s SCRIPT] [--enable-cache] [-l {off,all,output}] [-p {invokes,routines,kernels}] - [--backend {disable-validation,disable-indentation}] [-o OUTPUT_FILE] - [-api DSL] [-oalg OUTPUT_ALGORITHM_FILE] [-opsy OUTPUT_PSY_FILE] + [-o OUTPUT_FILE] [-api DSL] [-oalg OUTPUT_ALGORITHM_FILE] [-opsy OUTPUT_PSY_FILE] [-okern OUTPUT_KERNEL_PATH] [-dm] [-nodm] [--kernel-renaming {multiple,single}] [--log-level {OFF,DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-file LOG_FILE] [--keep-comments] [--keep-directives] [-I INCLUDE] [-d DIRECTORY] [--modman-file-ignore IGNORE_PATTERN] [--free-form | --fixed-form] - [--disable-intrinsic-required-args] [--enable-sign-intrinsic-argnames] + [--backend {disable-validation,disable-indentation}] [--disable-intrinsic-required-args] filename Transform a file using the PSyclone source-to-source Fortran compiler @@ -84,11 +84,6 @@ by the command: to apply line-length limit to output Fortran only. -p {invokes,routines,kernels}, --profile {invokes,routines,kernels} add profiling hooks for 'kernels', 'invokes' or 'routines' - --backend {disable-validation,disable-indentation} - options to control the PSyIR backend used for code generation. Use - 'disable-validation' to disable the validation checks that are - performed by default. Use 'disable-indentation' to turn off all - indentation in the generated code. -o OUTPUT_FILE (code-transformation mode) output file -api DSL, --psykal-dsl DSL whether to use a PSyKAl DSL (one of ['lfric', 'gocean']) @@ -124,18 +119,16 @@ by the command: --modman-file-ignore IGNORE_PATTERN Ignore files that contain the specified pattern. - Fortran Intrinsic output control: - These settings control how PSyclone outputs Fortran Intrinsics. The default behaviour - is to output named arguments for all intrinsics' arguments other than SIGN, - which will not have keyword arguments to support NEMO behaviour. - - --disable-intrinsic-required-args - Disables output code containing argument names for an intrinsic's - required arguments, i.e. SUM(arr, mask=maskarr) instead of - SUM(array=arr, mask=maskarr). This overrides any other options specified - for intrinsic output control. - --enable-sign-intrinsic-argnames - Enables adding argument names to the SIGN intrinsic's arguments. + Fortran backend control options.: + These settings control how PSyclone outputs Fortran. + + --backend {disable-validation,disable-indentation} + options to control the PSyIR backend used for code generation. + Use 'disable-validation' to disable the validation checks that are performed by + default. Use 'disable-indentation' to turn off all indentation in the generated code. + --disable-intrinsic-required-args + Disables output code containing argument names for an intrinsic's required arguments, + i.e. SUM(arr, mask=maskarr) instead of SUM(array=arr, mask=maskarr). Basic Use --------- diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index 4dd4ed8d29..ff45188024 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -487,13 +487,6 @@ def main(arguments): parser.add_argument( '-p', '--profile', action="append", choices=Profiler.SUPPORTED_OPTIONS, help="add profiling hooks for 'kernels', 'invokes' or 'routines'") - parser.add_argument( - '--backend', dest='backend', action="append", - choices=['disable-validation', 'disable-indentation'], - help=("options to control the PSyIR backend used for code generation. " - "Use 'disable-validation' to disable the validation checks that " - "are performed by default. Use 'disable-indentation' to turn off" - " all indentation in the generated code.")) # Code-transformation mode flags parser.add_argument('-o', metavar='OUTPUT_FILE', @@ -574,14 +567,18 @@ def main(arguments): "(default is to look at the input file extension)." ) - intrinsic_output_group = parser.add_argument_group( - "Fortran Intrinsic output control", - "These settings control how PSyclone outputs Fortran Intrinsics. " - "The default behaviour is to output named arguments for all " - "intrinsics' arguments other than SIGN, which will not have " - "keyword arguments to support NEMO behaviour." + backend_group = parser.add_argument_group( + "Fortran backend control options.", + "These settings control how PSyclone outputs Fortran. " ) - intrinsic_output_group.add_argument( + backend_group.add_argument( + '--backend', dest='backend', action="append", + choices=['disable-validation', 'disable-indentation'], + help=("options to control the PSyIR backend used for code generation. " + "Use 'disable-validation' to disable the validation checks that " + "are performed by default. Use 'disable-indentation' to turn off" + " all indentation in the generated code.")) + backend_group.add_argument( "--disable-intrinsic-required-args", default=argparse.SUPPRESS, action="store_true", help="Disables output code containing argument names for an " diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 386bf58e00..9b5e01d118 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3354,7 +3354,7 @@ def canonicalise(self): f"as non-optional argument name '{name}' found " f"but the Intrinsic has context-sensitive argument " f"names which is unsupported by PSyclone. Supplied " - f"intrinsic was '{self.debug_string().rstrip()}'." + f"intrinsic was a '{self.intrinsic.name}'." ) # The following rules are defined by the Fortran standard. diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 5e869c05e2..fc09ed3b52 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -2176,3 +2176,56 @@ def test_fw_schedule(fortran_reader, fortran_writer): schedule.addchild(child.copy().detach()) result = fortran_writer(schedule) assert result == test_code + + +def test_fw_intrinsiccall(fortran_reader, fortran_writer): + ''' Test that the FortranWriter correctly handles IntrinsicCall nodes, + and respects the config setting where possible.''' + # Test a piece of code where argument names are always required. + code = """subroutine foo() + real :: a, b, c + a = cshift(dim=1, shift=b, array=c) + end subroutine foo""" + + psyir = fortran_reader.psyir_from_source(code) + output = fortran_writer(psyir) + assert "a = CSHIFT(dim=1, shift=b, array=c)" in output + # Disable argument names in the config, we should get the same output. + Config.get().backend_intrinsic_named_kwargs = False + output = fortran_writer(psyir) + assert "a = CSHIFT(dim=1, shift=b, array=c)" in output + + # Test a piece of code where we can only remove the first argument name. + code = """subroutine foo() + real :: a, b, c + a = cshift(array=c, dim=1, shift=b) + end subroutine foo""" + psyir = fortran_reader.psyir_from_source(code) + output = fortran_writer(psyir) + assert "a = CSHIFT(c, dim=1, shift=b)" in output + + # Test a piece of code where can remove all required argument names. + code = """subroutine foo() + real :: a, b, c + a = cshift(array=c, shift=b, dim=1) + end subroutine foo""" + psyir = fortran_reader.psyir_from_source(code) + output = fortran_writer(psyir) + assert "a = CSHIFT(c, b, dim=1)" in output + # Check the config is respected + Config.get().backend_intrinsic_named_kwargs = True + output = fortran_writer(psyir) + assert "a = CSHIFT(array=c, shift=b, dim=1)" in output + + # Test if we have a non-canonicalisable intrinsic that it outputs + # all argument names (this is not a common use case, as most + # non canonicalisable intrinsics result in a code block, however + # its possible to obtain one during PSyIR creation). + Config.get().backend_intrinsic_named_kwargs = False + + intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) + IntrinsicCall._add_args( + intrinsic, [("scalar", + Reference(DataSymbol("b", INTEGER_TYPE)))]) + output = fortran_writer(intrinsic) + assert "ALLOCATED(scalar=b)" in output diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index eeff797014..a03bb908f4 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -670,7 +670,7 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " "'array' found but the Intrinsic has context-sensitive argument " "names which is unsupported by PSyclone. Supplied intrinsic was " - "'ALLOCATED(array=a)'." in str(err.value)) + "a 'ALLOCATED'." in str(err.value)) def test_canonicalisation(): From 77cf8a6bd9a9ab57887bbe7120a8df1b61c45eed Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Mon, 20 Oct 2025 12:53:16 +0100 Subject: [PATCH 31/50] Add documentation about overriding intrinsics --- doc/user_guide/psyclone_command.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/user_guide/psyclone_command.rst b/doc/user_guide/psyclone_command.rst index bd7843addf..c2967e9b73 100644 --- a/doc/user_guide/psyclone_command.rst +++ b/doc/user_guide/psyclone_command.rst @@ -530,3 +530,12 @@ some limitations: Note that using the ``keep-comments`` option alone means that any comments that PSyclone interprets as directives will be lost from the input. + +Overriding Fortran Intrinsics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +PSyclone attempts to canonicalise Fortran Intrinsics, which involves adding +argument names to each argument in the ``IntrinsicCall`` PSyIR node. This can +cause problems with code that overrides Fortran intrinsics. To ensure correct +behaviour of the output, the ``--disable-intrinsic-required-args`` option must +be passed to PSyclone, else the resultant code may not run correctly. From 1021c06c84e637c81a8f0763f6293156c927943c Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 7 Nov 2025 15:20:19 +0000 Subject: [PATCH 32/50] Changes for review --- doc/user_guide/psyclone_command.rst | 25 ++++++++++--------- src/psyclone/configuration.py | 2 +- src/psyclone/generator.py | 14 +++++++---- src/psyclone/psyir/backend/fortran.py | 2 +- src/psyclone/psyir/backend/visitor.py | 2 ++ src/psyclone/psyir/nodes/intrinsic_call.py | 12 +++++---- src/psyclone/tests/generator_test.py | 2 +- .../tests/psyir/backend/fortran_test.py | 11 +++++--- .../tests/psyir/nodes/intrinsic_call_test.py | 3 +-- 9 files changed, 43 insertions(+), 30 deletions(-) diff --git a/doc/user_guide/psyclone_command.rst b/doc/user_guide/psyclone_command.rst index c2967e9b73..4042ffb877 100644 --- a/doc/user_guide/psyclone_command.rst +++ b/doc/user_guide/psyclone_command.rst @@ -60,7 +60,7 @@ by the command: [--log-level {OFF,DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-file LOG_FILE] [--keep-comments] [--keep-directives] [-I INCLUDE] [-d DIRECTORY] [--modman-file-ignore IGNORE_PATTERN] [--free-form | --fixed-form] - [--backend {disable-validation,disable-indentation}] [--disable-intrinsic-required-args] + [--backend {disable-validation,disable-indentation}] [--disable-named-intrinsic-args] filename Transform a file using the PSyclone source-to-source Fortran compiler @@ -126,8 +126,9 @@ by the command: options to control the PSyIR backend used for code generation. Use 'disable-validation' to disable the validation checks that are performed by default. Use 'disable-indentation' to turn off all indentation in the generated code. - --disable-intrinsic-required-args - Disables output code containing argument names for an intrinsic's required arguments, + --disable-named-intrinsic-args + By default, the backend names any required arguments to intrinsic calls. This option + disables this feature (in case the processed code has overridden a Fortran intrinsic), i.e. SUM(arr, mask=maskarr) instead of SUM(array=arr, mask=maskarr). Basic Use @@ -324,6 +325,15 @@ The default behaviour may be changed by adding the :ref:`configuration file `. Note that any command-line setting always takes precedence. +Overriding Fortran Intrinsics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +PSyclone attempts to canonicalise Fortran Intrinsics, which involves adding +argument names to each argument in the ``IntrinsicCall`` PSyIR node. This can +cause problems with code that overrides Fortran intrinsics. To ensure correct +behaviour of the output, the ``--disable-named-intrinsic-args`` option must +be passed to PSyclone, else the resultant code may not compile or run correctly. + Automatic Profiling Instrumentation ----------------------------------- @@ -530,12 +540,3 @@ some limitations: Note that using the ``keep-comments`` option alone means that any comments that PSyclone interprets as directives will be lost from the input. - -Overriding Fortran Intrinsics -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -PSyclone attempts to canonicalise Fortran Intrinsics, which involves adding -argument names to each argument in the ``IntrinsicCall`` PSyIR node. This can -cause problems with code that overrides Fortran intrinsics. To ensure correct -behaviour of the output, the ``--disable-intrinsic-required-args`` option must -be passed to PSyclone, else the resultant code may not run correctly. diff --git a/src/psyclone/configuration.py b/src/psyclone/configuration.py index d1e02e6f09..264e4f08c6 100644 --- a/src/psyclone/configuration.py +++ b/src/psyclone/configuration.py @@ -236,7 +236,7 @@ def __init__(self): self._fortran_standard = None # By default, the PSyIR backends output argument names on (most) - # IntrinsicCalls. These two options enable control of tha behaviour. + # IntrinsicCalls. This option enables control of that behaviour. self._backend_intrinsic_named_kwargs = True # ------------------------------------------------------------------------- diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index 09ce88b1f0..8b609dfc38 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -585,11 +585,13 @@ def main(arguments): "are performed by default. Use 'disable-indentation' to turn off" " all indentation in the generated code.")) backend_group.add_argument( - "--disable-intrinsic-required-args", default=argparse.SUPPRESS, + "--disable-named-intrinsic-args", default=argparse.SUPPRESS, action="store_true", - help="Disables output code containing argument names for an " - "intrinsic's required arguments, i.e. SUM(arr, mask=maskarr) " - "instead of SUM(array=arr, mask=maskarr)." + help="By default, the backend names any required arguments to " + "intrinsic calls. This option disables this feature (in case " + "the processed code has overridden a Fortran intrinsic), " + "i.e. SUM(arr, mask=maskarr) instead of SUM(array=arr, " + "mask=maskarr)." ) args = parser.parse_args(arguments) @@ -644,7 +646,9 @@ def main(arguments): Config.get().api = api # Record any intrinsic output format settings. - if "disable_intrinsic_required_args" in args: + if "disable_named_intrinsic_args" in args: + # The backend won't attempt to add names to required + # arguments to Fortran intrinsics. Config.get().backend_intrinsic_named_kwargs = False # Record any profiling options. diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index cc561649b2..a5ed2ceddf 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1799,7 +1799,7 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: # Otherwise it is inside-expression function call return f"{self._visit(node.routine)}({args})" - def call_node(self, node) -> str: + def call_node(self, node: Call) -> str: '''Translate the PSyIR call node to Fortran. :param node: a Call PSyIR node. diff --git a/src/psyclone/psyir/backend/visitor.py b/src/psyclone/psyir/backend/visitor.py index af602e8615..a577b9bd04 100644 --- a/src/psyclone/psyir/backend/visitor.py +++ b/src/psyclone/psyir/backend/visitor.py @@ -262,6 +262,7 @@ def _visit(self, node): # Check global constraints for this node (if validation enabled). if self._validate_nodes: node.validate_global_constraints() + # Make a list of the node's ancestor classes (including # itself) in method resolution order (mro), apart from the # base "object" class. @@ -274,6 +275,7 @@ def _visit(self, node): for method_name in possible_method_names: try: node_result = getattr(self, method_name)(node) + # We can only proceed to add comments if the Visitor # returned a string, otherwise we just return if not isinstance(node_result, str): diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 472d5ffe61..0e22fbc7da 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3192,12 +3192,14 @@ def _find_matching_interface(self) -> Tuple[str]: for name in self.argument_names: if not name: continue + # Need to check lower case. + lname = name.lower() # Optional argument names are skipped over as they don't # affect which interface is being used. - if name in optional_names: + if lname in optional_names: continue for arglist in potential_interfaces: - if name not in arglist: + if lname not in arglist: potential_interfaces.remove(arglist) # Remove any of the interfaces that have too many or @@ -3234,7 +3236,8 @@ def _find_matching_interface(self) -> Tuple[str]: # this interface cannot be a candidate for canonicalisation. remaining_required = choice[num_positional_arguments:] for name in remaining_required: - if name not in self.argument_names: + lname = name.lower() + if lname not in self.argument_names: potential_interfaces.remove(choice) break @@ -3353,8 +3356,7 @@ def canonicalise(self): f"Cannot canonicalise '{self.intrinsic.name}' " f"as non-optional argument name '{name}' found " f"but the Intrinsic has context-sensitive argument " - f"names which is unsupported by PSyclone. Supplied " - f"intrinsic was a '{self.intrinsic.name}'." + f"names which is unsupported by PSyclone." ) # The following rules are defined by the Fortran standard. diff --git a/src/psyclone/tests/generator_test.py b/src/psyclone/tests/generator_test.py index 0b8f60993a..65427ec7ac 100644 --- a/src/psyclone/tests/generator_test.py +++ b/src/psyclone/tests/generator_test.py @@ -2059,5 +2059,5 @@ def test_intrinsic_control_settings(tmpdir, caplog): filename = str(tmpdir.join("test.f90")) with open(filename, "w", encoding='utf-8') as my_file: my_file.write(code) - main([filename, "--disable-intrinsic-required-args"]) + main([filename, "--disable-named-intrinsic-args"]) assert Config.get().backend_intrinsic_named_kwargs is False diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index fc09ed3b52..8e9ea15b48 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -1852,10 +1852,10 @@ def test_fw_intrinsic_output_control(fortran_writer): call = IntrinsicCall.create(IntrinsicCall.Intrinsic.ISHFT, args) result = fortran_writer(call) - # Default behaviour is to include argument names. + # Default behaviour is to include names of all arguments. assert "ISHFT(i=arg1, shift=arg2)" in result - # Turn off the output of required arguments + # Turn off the output of names of required arguments Config.get().backend_intrinsic_named_kwargs = False result = fortran_writer(call) assert "ISHFT(arg1, arg2)" in result @@ -2207,7 +2207,7 @@ def test_fw_intrinsiccall(fortran_reader, fortran_writer): # Test a piece of code where can remove all required argument names. code = """subroutine foo() real :: a, b, c - a = cshift(array=c, shift=b, dim=1) + a = cshift(aRRay=c, SHIFT=b, dim=1) end subroutine foo""" psyir = fortran_reader.psyir_from_source(code) output = fortran_writer(psyir) @@ -2227,5 +2227,10 @@ def test_fw_intrinsiccall(fortran_reader, fortran_writer): IntrinsicCall._add_args( intrinsic, [("scalar", Reference(DataSymbol("b", INTEGER_TYPE)))]) + # Ensure this cannot be canonicalised. + with pytest.raises(NotImplementedError) as err: + intrinsic.canonicalise() + assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " + "'scalar' found" in str(err.value)) output = fortran_writer(intrinsic) assert "ALLOCATED(scalar=b)" in output diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index c1ff509f94..b44624bc0e 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -669,8 +669,7 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): intrinsic.canonicalise() assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " "'array' found but the Intrinsic has context-sensitive argument " - "names which is unsupported by PSyclone. Supplied intrinsic was " - "a 'ALLOCATED'." in str(err.value)) + "names which is unsupported by PSyclone." in str(err.value)) def test_canonicalisation(): From 99ff7b02da31e6a9fedf3ec259380410b54665e2 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 7 Nov 2025 15:24:12 +0000 Subject: [PATCH 33/50] Emphasize arg names must be lower case in definitions of Intrinsics --- src/psyclone/psyir/nodes/intrinsic_call.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 0e22fbc7da..d5e6ec625b 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -109,6 +109,9 @@ class Intrinsic(IAttr, Enum): Enum must have a different value, and without the name that would not be guaranteed. + All argument names (i.e. required_args.arg_names and + optional_args) should be lower case. + ''' # Fortran special-case statements (technically not Fortran intrinsics # but in PSyIR they are represented as Intrinsics) From ce27a54845609087a4b34b1d11a37398f447f749 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 7 Nov 2025 15:25:52 +0000 Subject: [PATCH 34/50] linting --- src/psyclone/psyir/nodes/intrinsic_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index d5e6ec625b..528e68a95b 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -109,7 +109,7 @@ class Intrinsic(IAttr, Enum): Enum must have a different value, and without the name that would not be guaranteed. - All argument names (i.e. required_args.arg_names and + All argument names (i.e. required_args.arg_names and optional_args) should be lower case. ''' From 33ddc807842950fb835cff20c55076826f637725 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 11 Nov 2025 14:54:59 +0000 Subject: [PATCH 35/50] Test updating the integration --- .github/workflows/nemo_tests.yml | 17 +++++++++++++- .github/workflows/nemo_v5_tests.yml | 17 +++++++++++--- src/psyclone/generator.py | 34 ++++++++++++++++------------ src/psyclone/tests/generator_test.py | 13 ++++------- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/.github/workflows/nemo_tests.yml b/.github/workflows/nemo_tests.yml index 5c02124e25..0fda5d092f 100644 --- a/.github/workflows/nemo_tests.yml +++ b/.github/workflows/nemo_tests.yml @@ -110,6 +110,9 @@ jobs: module load nvidia-hpcsdk/${NVFORTRAN_VERSION} module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} passthrough make -j ${NUM_PARALLEL} compile-passthrough make run-passthrough @@ -128,6 +131,9 @@ jobs: module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} make clean + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script make -j ${NUM_PARALLEL} openmp_gpu make -j ${NUM_PARALLEL} compile-openmp_gpu @@ -152,6 +158,9 @@ jobs: module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} make clean + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/acc_kernels_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} openacc_kernels COMPILER_ARCH=linux_nvidia_acc_gpu make -j ${NUM_PARALLEL} compile-openacc_kernels export NV_ACC_POOL_THRESHOLD=75 @@ -179,6 +188,9 @@ jobs: module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} make clean + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/acc_loops_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} openacc_loops COMPILER_ARCH=linux_nvidia_acc_gpu make -j ${NUM_PARALLEL} compile-openacc_loops export NV_ACC_POOL_THRESHOLD=75 @@ -207,7 +219,7 @@ jobs: export PSYCLONE_NEMO_DIR=${GITHUB_WORKSPACE}/examples/nemo/scripts export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py -I ${MPI_HOME}/include" + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -r8 -O2 -heap-arrays -fp-model=precise -g -qopenmp" export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script @@ -240,6 +252,9 @@ jobs: make clean export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script export ASYNC_PARALLEL=1 + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} openmp_gpu make -j ${NUM_PARALLEL} compile-openmp_gpu export NV_ACC_POOL_THRESHOLD=75 diff --git a/.github/workflows/nemo_v5_tests.yml b/.github/workflows/nemo_v5_tests.yml index dc918cb841..424d64feed 100644 --- a/.github/workflows/nemo_v5_tests.yml +++ b/.github/workflows/nemo_v5_tests.yml @@ -139,6 +139,9 @@ jobs: # Set up FCM: PATHs are loaded from SPACK, we only need to set the FCFLAGS cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/passthrough.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -Mr8 -O2 -nofma -Mnovect -g" # Clean up and compile @@ -171,6 +174,9 @@ jobs: # Set up FCM: PATHs are loaded from SPACK, we only need to set the FCFLAGS cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm + export PSYCLONE_COMPILER=$MPIF90 + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/passthrough.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -r8 -O2 -fp-model precise -fno-alias -g" # Clean up and compile @@ -202,7 +208,7 @@ jobs: cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py" + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-fdefault-real-8 -O2 -fcray-pointer -ffree-line-length-none -g -fopenmp" # Clean up and compile @@ -247,7 +253,7 @@ jobs: export REPRODUCIBLE=1 export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py" + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" # Clean up and compile rm -rf tests/${TEST_DIR} ./makenemo -r BENCH -m linux_spack_profile -n ${TEST_DIR} -j ${NUM_PARALLEL} -v 1 @@ -297,6 +303,8 @@ jobs: # We compile at "-O2 -Mnofma -Mnovect -gpu=math_uniform" to permit comparison of the results. cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -Mr8 -O2 -Mnofma -Mnovect -g -mp=gpu -gpu=mem:managed,math_uniform" export REPRODUCIBLE=1 @@ -332,6 +340,8 @@ jobs: # We compile at "-O2 -Mnofma -Mnovect -gpu=math_uniform" to permit comparison of the results. cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm + export MPIF90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -Mr8 -O2 -Mnofma -Mnovect -g -mp=gpu -gpu=mem:managed,math_uniform" export REPRODUCIBLE=1 @@ -367,7 +377,8 @@ jobs: export PROFILING_DIR=${GITHUB_WORKSPACE}/lib/profiling/nvidia/ cd $PROFILING_DIR make clean - F90=$MPIF90 make + export F90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" # First do a debug-build: set the environemnt variables to use flags and intrinsics # with numerically reproducible results and enable PROFILING hooks diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index 8b609dfc38..c06eb66c05 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -578,14 +578,19 @@ def main(arguments): "These settings control how PSyclone outputs Fortran. " ) backend_group.add_argument( - '--backend', dest='backend', action="append", - choices=['disable-validation', 'disable-indentation'], - help=("options to control the PSyIR backend used for code generation. " - "Use 'disable-validation' to disable the validation checks that " - "are performed by default. Use 'disable-indentation' to turn off" - " all indentation in the generated code.")) + "--backend-disable-validation", default=argparse.SUPPRESS, + action="store_true", + help=("Disables validation checks that PSyclone backends perform by " + "default.") + ) + backend_group.add_argument( + "--backend-disable-indentation", default=argparse.SUPPRESS, + action="store_true", + help="Disables all indentation in the generated output code." + ) backend_group.add_argument( - "--disable-named-intrinsic-args", default=argparse.SUPPRESS, + "--backend-omit-unneeded-intrinsic-arg-names", + default=argparse.SUPPRESS, action="store_true", help="By default, the backend names any required arguments to " "intrinsic calls. This option disables this feature (in case " @@ -646,7 +651,7 @@ def main(arguments): Config.get().api = api # Record any intrinsic output format settings. - if "disable_named_intrinsic_args" in args: + if "backend_omit_unneeded_intrinsic_arg_names" in args: # The backend won't attempt to add names to required # arguments to Fortran intrinsics. Config.get().backend_intrinsic_named_kwargs = False @@ -658,13 +663,12 @@ def main(arguments): except ValueError as err: print(f"Invalid profiling option: {err}", file=sys.stderr) sys.exit(1) - if args.backend: - # A command-line flag overrides the setting in the Config file (if - # any). - if "disable-validation" in args.backend: - Config.get().backend_checks_enabled = False - if "disable-indentation" in args.backend: - Config.get().backend_indentation_disabled = True + # A command-line flag overrides the setting in the Config file (if + # any). + if "backend_disable_validation" in args: + Config.get().backend_checks_enabled = False + if "backend_disable_indentation" in args: + Config.get().backend_indentation_disabled = True # The Configuration manager checks that the supplied path(s) is/are # valid so protect with a try diff --git a/src/psyclone/tests/generator_test.py b/src/psyclone/tests/generator_test.py index 65427ec7ac..d3cc485898 100644 --- a/src/psyclone/tests/generator_test.py +++ b/src/psyclone/tests/generator_test.py @@ -993,21 +993,16 @@ def test_main_directory_arg(capsys): def test_main_backend_arg(capsys): '''Test the --backend options in main().''' filename = os.path.join(LFRIC_BASE_PATH, "1_single_invoke.f90") - with pytest.raises(SystemExit): - main([filename, "-api", "lfric", "--backend", "invalid"]) - _, output = capsys.readouterr() - assert "--backend: invalid choice: 'invalid'" in output - # Make sure we get a default config instance Config._instance = None # Default is to have checks enabled. assert Config.get().backend_checks_enabled is True - main([filename, "-api", "lfric", "--backend", "disable-validation"]) + main([filename, "-api", "lfric", "--backend-disable-validation"]) assert Config.get().backend_checks_enabled is False assert Config.get().backend_indentation_disabled is False Config._instance = None filename = os.path.join(NEMO_BASE_PATH, "explicit_do_long_line.f90") - main([filename, "--backend", "disable-indentation"]) + main([filename, "--backend-disable-indentation"]) output, _ = capsys.readouterr() # None of the three DO loops should be indented. assert len(re.findall(r"^do j", output, re.MULTILINE)) == 3 @@ -1305,7 +1300,7 @@ def dummy_fortran_writer(check_global_constraints: bool, if validate: options = [] else: - options = ["--backend", "disable-validation"] + options = ["--backend-disable-validation"] main([str(input_file)] + options) # The actual assert is in the dummy_fortran_writer function above @@ -2059,5 +2054,5 @@ def test_intrinsic_control_settings(tmpdir, caplog): filename = str(tmpdir.join("test.f90")) with open(filename, "w", encoding='utf-8') as my_file: my_file.write(code) - main([filename, "--disable-named-intrinsic-args"]) + main([filename, "--backend-omit-unneeded-intrinsic-arg-names"]) assert Config.get().backend_intrinsic_named_kwargs is False From 2bbdbc069b777b1e11f0f702f717450d903bdf54 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 11 Nov 2025 16:47:18 +0000 Subject: [PATCH 36/50] try again? --- .github/workflows/nemo_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nemo_tests.yml b/.github/workflows/nemo_tests.yml index 0fda5d092f..e331a87e78 100644 --- a/.github/workflows/nemo_tests.yml +++ b/.github/workflows/nemo_tests.yml @@ -133,7 +133,8 @@ jobs: make clean export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" + export F90=psyclonefc + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script make -j ${NUM_PARALLEL} openmp_gpu make -j ${NUM_PARALLEL} compile-openmp_gpu From eba7d1e0fa768feea9a88e1f62ed9c66b3dd85cc Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 12 Nov 2025 14:50:59 +0000 Subject: [PATCH 37/50] Revert changes to integration. Fix bug sergi found and add tests --- .github/workflows/nemo_tests.yml | 18 +----------------- .github/workflows/nemo_v5_tests.yml | 17 +++-------------- src/psyclone/psyir/backend/fortran.py | 11 +++++++---- src/psyclone/psyir/nodes/intrinsic_call.py | 2 ++ .../tests/psyir/backend/fortran_test.py | 7 +++++++ 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/.github/workflows/nemo_tests.yml b/.github/workflows/nemo_tests.yml index e331a87e78..5c02124e25 100644 --- a/.github/workflows/nemo_tests.yml +++ b/.github/workflows/nemo_tests.yml @@ -110,9 +110,6 @@ jobs: module load nvidia-hpcsdk/${NVFORTRAN_VERSION} module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} passthrough make -j ${NUM_PARALLEL} compile-passthrough make run-passthrough @@ -131,10 +128,6 @@ jobs: module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} make clean - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export F90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script make -j ${NUM_PARALLEL} openmp_gpu make -j ${NUM_PARALLEL} compile-openmp_gpu @@ -159,9 +152,6 @@ jobs: module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} make clean - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/acc_kernels_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} openacc_kernels COMPILER_ARCH=linux_nvidia_acc_gpu make -j ${NUM_PARALLEL} compile-openacc_kernels export NV_ACC_POOL_THRESHOLD=75 @@ -189,9 +179,6 @@ jobs: module load hdf5/${HDF5_VERSION} netcdf-c/${NETCDF_C_VERSION} netcdf-fortran/${NETCDF_FORTRAN_VERSION} module load perl/${PERL_VERSION} make clean - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/acc_loops_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} openacc_loops COMPILER_ARCH=linux_nvidia_acc_gpu make -j ${NUM_PARALLEL} compile-openacc_loops export NV_ACC_POOL_THRESHOLD=75 @@ -220,7 +207,7 @@ jobs: export PSYCLONE_NEMO_DIR=${GITHUB_WORKSPACE}/examples/nemo/scripts export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py -I ${MPI_HOME}/include" export FCFLAGS="-i4 -r8 -O2 -heap-arrays -fp-model=precise -g -qopenmp" export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script @@ -253,9 +240,6 @@ jobs: make clean export NEMOV4=1 # Enables specific NEMOV4 exclusions in the PSyclone transformation script export ASYNC_PARALLEL=1 - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py -I ${MPI_HOME}/include --backend-omit-unneeded-intrinsic-arg-names" make -j ${NUM_PARALLEL} openmp_gpu make -j ${NUM_PARALLEL} compile-openmp_gpu export NV_ACC_POOL_THRESHOLD=75 diff --git a/.github/workflows/nemo_v5_tests.yml b/.github/workflows/nemo_v5_tests.yml index 424d64feed..dc918cb841 100644 --- a/.github/workflows/nemo_v5_tests.yml +++ b/.github/workflows/nemo_v5_tests.yml @@ -139,9 +139,6 @@ jobs: # Set up FCM: PATHs are loaded from SPACK, we only need to set the FCFLAGS cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/passthrough.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -Mr8 -O2 -nofma -Mnovect -g" # Clean up and compile @@ -174,9 +171,6 @@ jobs: # Set up FCM: PATHs are loaded from SPACK, we only need to set the FCFLAGS cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm - export PSYCLONE_COMPILER=$MPIF90 - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/passthrough.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -r8 -O2 -fp-model precise -fno-alias -g" # Clean up and compile @@ -208,7 +202,7 @@ jobs: cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py" export FCFLAGS="-fdefault-real-8 -O2 -fcray-pointer -ffree-line-length-none -g -fopenmp" # Clean up and compile @@ -253,7 +247,7 @@ jobs: export REPRODUCIBLE=1 export PSYCLONE_COMPILER=$MPIF90 export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_cpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" + export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py" # Clean up and compile rm -rf tests/${TEST_DIR} ./makenemo -r BENCH -m linux_spack_profile -n ${TEST_DIR} -j ${NUM_PARALLEL} -v 1 @@ -303,8 +297,6 @@ jobs: # We compile at "-O2 -Mnofma -Mnovect -gpu=math_uniform" to permit comparison of the results. cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -Mr8 -O2 -Mnofma -Mnovect -g -mp=gpu -gpu=mem:managed,math_uniform" export REPRODUCIBLE=1 @@ -340,8 +332,6 @@ jobs: # We compile at "-O2 -Mnofma -Mnovect -gpu=math_uniform" to permit comparison of the results. cd $NEMO_DIR cp $PSYCLONE_NEMO_DIR/KGOs/arch-linux_spack.fcm arch/arch-linux_spack.fcm - export MPIF90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" export FCFLAGS="-i4 -Mr8 -O2 -Mnofma -Mnovect -g -mp=gpu -gpu=mem:managed,math_uniform" export REPRODUCIBLE=1 @@ -377,8 +367,7 @@ jobs: export PROFILING_DIR=${GITHUB_WORKSPACE}/lib/profiling/nvidia/ cd $PROFILING_DIR make clean - export F90=psyclonefc - export PSYCLONE_OPTS="--enable-cache -l output -s ${PSYCLONE_NEMO_DIR}/omp_gpu_trans.py --backend-omit-unneeded-intrinsic-arg-names" + F90=$MPIF90 make # First do a debug-build: set the environemnt variables to use flags and intrinsics # with numerically reproducible results and enable PROFILING hooks diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index a5ed2ceddf..2df938ae0e 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1773,10 +1773,13 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: # behaviour from here. correct_names = False # Otherwise, use the default behaviour. - args.append( - f"{node.argument_names[idx]}=" - f"{self._visit(node.arguments[idx])}" - ) + if node.argument_names[idx]: + args.append( + f"{node.argument_names[idx]}=" + f"{self._visit(node.arguments[idx])}" + ) + else: + args.append(f"{self._visit(node.arguments[idx])}") args = ", ".join(args) except NotImplementedError: # If the Intrinsic fails to canonicalise, or to match diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 528e68a95b..39cc550894 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3391,6 +3391,8 @@ def canonicalise(self): self._argument_names[i] = (self._argument_names[i][0], optional_names[i - len( interface_arg_names)]) + if self.intrinsic.name == "MAX": + print(self.argument_names) @classmethod def create(cls, intrinsic, arguments=()): diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 8e9ea15b48..f90c63165e 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -1933,6 +1933,13 @@ def test_fw_intrinsic_call_node(fortran_writer): gen = fortran_writer(rcall) assert gen == "call RANDOM_NUMBER(harvest=var)\n" + # Test an intrinsic with no cap on arguments. + rcall = IntrinsicCall.create(IntrinsicCall.Intrinsic.MAX, + [Reference(sym), Reference(sym), + Reference(sym), Reference(sym)]) + gen = fortran_writer(rcall) + assert gen == "MAX(var, var, var, var)\n" + for intrinsic_function in [IntrinsicCall.Intrinsic.MINVAL, IntrinsicCall.Intrinsic.MAXVAL, IntrinsicCall.Intrinsic.SUM]: From e6276e66bb4eee2629d5d6f790bcb6b4aeb6158c Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 12 Nov 2025 16:05:54 +0000 Subject: [PATCH 38/50] Updated canoncalisation name --- src/psyclone/psyir/backend/fortran.py | 10 +- src/psyclone/psyir/backend/sympy_writer.py | 9 +- src/psyclone/psyir/frontend/fparser2.py | 2 +- src/psyclone/psyir/nodes/intrinsic_call.py | 65 ++++----- .../intrinsics/array_reduction_base_trans.py | 4 +- .../tests/psyir/backend/fortran_test.py | 13 +- .../tests/psyir/backend/sympy_writer_test.py | 9 +- .../tests/psyir/nodes/intrinsic_call_test.py | 124 +++++++++--------- 8 files changed, 123 insertions(+), 113 deletions(-) diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 2df938ae0e..3d7b4e5b7b 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1756,9 +1756,9 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: try: # Canonicalisation handles any error checking we might # otherwise want to try. Most IntrinsicCalls should already - # be canonicalised, but we do it here to ensure that it is - # is possible. - node.canonicalise() + # have argument names added, but we do it here to ensure that + # it is is possible. + node.compute_argument_names() intrinsic_interface = node._find_matching_interface() args = [] correct_names = True @@ -1782,8 +1782,8 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: args.append(f"{self._visit(node.arguments[idx])}") args = ", ".join(args) except NotImplementedError: - # If the Intrinsic fails to canonicalise, or to match - # to an interface, then use the default behaviour. + # If the Intrinsic fails to have argument names added, or to + # match to an interface, then use the default behaviour. args = self._gen_arguments(node) else: args = self._gen_arguments(node) diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index f8d5693f50..321bc5b9d8 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -760,14 +760,15 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: :returns: the SymPy representation for the Intrinsic. ''' - # Force canonicalisation of the intrinsic + # Add argument names to the intrinsic try: - node.canonicalise() + node.compute_argument_names() except (GenerationError, NotImplementedError) as err: raise VisitorError( f"Sympy handler can't handle an IntrinsicCall that " - f"can't be canonicalised. Use explicit argument names " - f"to force canonicalisation. Failing node was " + f"can't have argument names automatically added. Use " + f"explicit argument names instead. " + f"Failing node was " f"'{node.debug_string()}'.") from err # Sympy does not support argument names, remove them for now diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index 5dac15cad8..a3ecc88f94 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -4899,7 +4899,7 @@ def _intrinsic_handler(self, node, parent): call = IntrinsicCall(intrinsic, parent=parent) call = self._process_args(node, call, True) - call.canonicalise() + call.compute_argument_names() return call except KeyError as err: raise NotImplementedError( diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 39cc550894..809846691c 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -70,8 +70,8 @@ # then `max_count` will be None. If max_count is not None, then arg_names # will contain a list of the argument names of the required arguments, in # the order defined by the standard. If max_count is None, arg_names will -# be a tuple containing None to ensure the canonicalisation logic still -# works. +# be a tuple containing None to ensure the argument name computation logic +# still works. ArgDesc = namedtuple('ArgDesc', 'min_count max_count types arg_names') @@ -3176,7 +3176,7 @@ def is_available_on_device(self, device_string: str = "") -> bool: def _find_matching_interface(self) -> Tuple[str]: ''' Finds the matching required argument interface for this node to - canonicalise to. + add argument names from. :raises NotImplementedError: if there is not exactly one argument interface that matches this @@ -3236,7 +3236,7 @@ def _find_matching_interface(self) -> Tuple[str]: # potential interface that are not already matched to a # positional argument in this IntrinsicCall. These must # be matched to named arguments in this IntrinsicCall, else - # this interface cannot be a candidate for canonicalisation. + # this interface cannot be a candidate for argument names. remaining_required = choice[num_positional_arguments:] for name in remaining_required: lname = name.lower() @@ -3244,12 +3244,12 @@ def _find_matching_interface(self) -> Tuple[str]: potential_interfaces.remove(choice) break - # If we didn't reduce the number of potential interfacfes to a - # single interface then we can't canonicalise. + # If we didn't reduce the number of potential interfaces to a + # single interface then we can't add argument names. if (len(potential_interfaces) > 1 or len(potential_interfaces) == 0): raise NotImplementedError( - f"Cannot canonicalise '{self.intrinsic.name}' " + f"Cannot add argument names to '{self.intrinsic.name}' " f"IntrinsicCall as PSyclone can't determine which " f"argument set it should use. This can be resolved by " f"using named arguments in the Fortran source." @@ -3262,17 +3262,18 @@ def _find_matching_interface(self) -> Tuple[str]: # This intrinsic has no required arguments. return () - def canonicalise(self): - '''Canonicalise an IntrinsicCall in the PSyIR. Upon successful - canonicalisation, all arguments will become named arguments. + def compute_argument_names(self): + '''Computes the argument names that correspond to the arguments + of this IntrinsicCall, and add those argument names. If the interface + is ambiguous, this function will raise an error. - A small number of intrinsics (e.g. ALLOCATE) never have ambiguity - and no argument limits, in which case no canonicalisation is done. + A small number of intrinsics (e.g. ALLOCATE, MAX) never have ambiguity + and no argument limits, in which case no argument names are added. :raises ValueError: If the number of arguments or argument names are not valid for this IntrinsicCall. :raises NotImplementedError: If there is argument ambiguity and - canonicalisation is not possible. + computation of argument names is not possible. ''' # First step is to convert all the argument names in the # intrinsic call to lower case. This also avoids constant @@ -3306,23 +3307,23 @@ def canonicalise(self): if name not in all_valid_names: raise ValueError( f"Found invalid argument name '{name}' when " - f"canonicalising the '{self.intrinsic.name}' " - f"IntrinsicCall. Allowed argument names are " - f"'{sorted(set(all_valid_names))}'." + f"computing argument names for the " + f"'{self.intrinsic.name}' IntrinsicCall. Allowed " + f"argument names are '{sorted(set(all_valid_names))}'." ) # Check that this call has a valid number of arguments if len(self.arguments) < self.intrinsic.required_args.min_count: raise ValueError( - f"Found too few arguments when canonicalising the " - f"'{self.intrinsic.name}' IntrinsicCall. Requires at " + f"Found too few arguments when computing argument names for " + f"the '{self.intrinsic.name}' IntrinsicCall. Requires at " f"least {self.intrinsic.required_args.min_count} " f"arguments but found {len(self.arguments)}." ) # If there is no maximum number of required arguments then we - # can skip the rest of canonicalisation, as this Intrinsic can never - # have ambiguity. + # can skip the rest of argument name computation, as this Intrinsic + # can never have ambiguity. if self.intrinsic.required_args.max_count is None: return @@ -3331,12 +3332,12 @@ def canonicalise(self): max_args = (self.intrinsic.required_args.max_count + len(optional_names)) raise ValueError( - f"Found too many arguments when canonicalising the " - f"'{self.intrinsic.name}' IntrinsicCall. Requires at most " - f"{max_args} arguments but found {len(self.arguments)}." + f"Found too many arguments when computing argument names " + f"for the '{self.intrinsic.name}' IntrinsicCall. Requires at " + f"most {max_args} arguments but found {len(self.arguments)}." ) - # Find which intrinsic call interface we are canonicalising with. + # Find which intrinsic call interface we matching arguments to. interface_arg_names = self._find_matching_interface() # Handle cases where None or "" is in the interface_arg_names, @@ -3344,19 +3345,21 @@ def canonicalise(self): # cannot handle. if interface_arg_names and not interface_arg_names[0]: # If we find any named non-optional named arguments for these - # intrinsics then we can't canonicalise this IntrinsicCall. - # N.B. With currently supported intrinsic there are no + # intrinsics then we can't add argument names to this + # IntrinsicCall. + # N.B. With currently supported intrinsics there are no # optional argument on these context-sensitive intrinsics # that have a finite argument count, but we keep the check # in case we need the support in future, and it still handles # what we currently need to check (i.e. if we have a named - # argument here we can't canonicalise it safely). + # argument here we can't add argument names to it safely). for name in self.argument_names: if not name: continue if name not in optional_names: raise NotImplementedError( - f"Cannot canonicalise '{self.intrinsic.name}' " + f"Cannot add argument names to " + f"'{self.intrinsic.name}' " f"as non-optional argument name '{name}' found " f"but the Intrinsic has context-sensitive argument " f"names which is unsupported by PSyclone." @@ -3474,11 +3477,11 @@ def create(cls, intrinsic, arguments=()): # the intrinsic enum. call._add_args(call, arguments) - # Error check and canonicalise the call + # Error check and add argument names to the call try: - call.canonicalise() + call.compute_argument_names() except (ValueError, NotImplementedError): - # Since we fail canonicalisation, we need to undo any links + # Since we fail adding argument names, we need to undo any links # created between nodes and return all inputs to their original # state before raising the error to the caller. for child in call.children: diff --git a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py index 81e2ac8d5a..3fec8454b7 100644 --- a/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py +++ b/src/psyclone/psyir/transformations/intrinsics/array_reduction_base_trans.py @@ -82,8 +82,8 @@ def _get_args(node): # Determine the arguments to the intrinsic args = [None, None, None] arg_names_map = {"array": 0, "dim": 1, "mask": 2} - # Canonicalise the intrinsic to resolve the names. - node.canonicalise() + # Add argument names to the intrinsic. + node.compute_argument_names() for idx, child in enumerate(node.arguments): name = node.argument_names[idx].lower() args[arg_names_map[name]] = child diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index f90c63165e..805b9c88e5 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -2224,9 +2224,10 @@ def test_fw_intrinsiccall(fortran_reader, fortran_writer): output = fortran_writer(psyir) assert "a = CSHIFT(array=c, shift=b, dim=1)" in output - # Test if we have a non-canonicalisable intrinsic that it outputs + # Test if we have an intrinsic that can't have argument names computed + # that it outputs # all argument names (this is not a common use case, as most - # non canonicalisable intrinsics result in a code block, however + # intrinsics like this result in a code block, however # its possible to obtain one during PSyIR creation). Config.get().backend_intrinsic_named_kwargs = False @@ -2234,10 +2235,10 @@ def test_fw_intrinsiccall(fortran_reader, fortran_writer): IntrinsicCall._add_args( intrinsic, [("scalar", Reference(DataSymbol("b", INTEGER_TYPE)))]) - # Ensure this cannot be canonicalised. + # Ensure this cannot have argument names computed. with pytest.raises(NotImplementedError) as err: - intrinsic.canonicalise() - assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " - "'scalar' found" in str(err.value)) + intrinsic.compute_argument_names() + assert ("Cannot add argument names to 'ALLOCATED' as non-optional " + "argument name 'scalar' found" in str(err.value)) output = fortran_writer(intrinsic) assert "ALLOCATED(scalar=b)" in output diff --git a/src/psyclone/tests/psyir/backend/sympy_writer_test.py b/src/psyclone/tests/psyir/backend/sympy_writer_test.py index c3d8928e35..3cc4b875c3 100644 --- a/src/psyclone/tests/psyir/backend/sympy_writer_test.py +++ b/src/psyclone/tests/psyir/backend/sympy_writer_test.py @@ -333,7 +333,8 @@ def test_sympy_writer_type_map(expr, sym_map, fortran_reader): def test_sympy_writer_type_map_non_canonical(fortran_reader): - ''' Test we get an error when the intrinsic can't be canonicalised.''' + ''' Test we get an error when the intrinsic can't have argument names + computed.''' source = """program test_prog use my_mod integer :: i, j, k @@ -352,9 +353,9 @@ def test_sympy_writer_type_map_non_canonical(fortran_reader): writer = SymPyWriter() with pytest.raises(VisitorError) as err: _ = writer([assign.rhs]) - assert ("Sympy handler can't handle an IntrinsicCall that can't be " - "canonicalised. Use explicit argument names to force " - "canonicalisation. Failing node was 'SUM(i, j)'." + assert ("Sympy handler can't handle an IntrinsicCall that can't have " + "argument names automatically added. Use explicit argument names " + "instead. Failing node was 'SUM(i, j)'." in str(err.value)) diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index b44624bc0e..478edfe499 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -353,16 +353,16 @@ def test_intrinsiccall_create_errors(): # An allocate must have one or more References as argument. with pytest.raises(ValueError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.ALLOCATE, []) - assert ("Found too few arguments when canonicalising the 'ALLOCATE' " - "IntrinsicCall. Requires at least 1 arguments but found 0." - in str(err.value)) + assert ("Found too few arguments when computing argument names for " + "the 'ALLOCATE' IntrinsicCall. Requires at least 1 arguments " + "but found 0." in str(err.value)) # The random intrinsic only accepts one argument. with pytest.raises(ValueError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, [aref, aref.copy()]) - assert ("Found too many arguments when canonicalising the 'RANDOM_NUMBER'" - " IntrinsicCall. Requires at most 1 arguments but found 2." - in str(err.value)) + assert ("Found too many arguments when computing argument names for the " + "'RANDOM_NUMBER' IntrinsicCall. Requires at most 1 arguments but " + "found 2." in str(err.value)) # Wrong type for a positional argument. with pytest.raises(TypeError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.ALLOCATE, @@ -387,9 +387,9 @@ def test_intrinsiccall_create_errors(): with pytest.raises(ValueError) as err: IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, [aref.detach(), ("willow", Reference(sym))]) - assert ("Found invalid argument name 'willow' when canonicalising the " - "'RANDOM_NUMBER' IntrinsicCall. Allowed argument names are " - "'['harvest']'." in str(err.value)) + assert ("Found invalid argument name 'willow' when computing argument " + "names for the 'RANDOM_NUMBER' IntrinsicCall. Allowed argument " + "names are '['harvest']'." in str(err.value)) # Wrong type for the name of an optional argument. with pytest.raises(TypeError) as err: @@ -585,58 +585,59 @@ def test_verify_intrinsic(fortran_reader, fortran_writer): "kind=kind(x=1), back=.true.) == 0) then" in result) -def test_intrinsic_canonicalisation_value_errors(): +def test_intrinsic_compute_argument_names_value_errors(): ''' - Test the canonicalisation function of the IntrinsicCall class raises + Test the compute_argument_names function of the IntrinsicCall class raises ValueErrors with bad inputs. ''' - # Test canonicalisation fails if we have an incorrect name argument. + # Test argument name computation fails if we have an incorrect named + # argument. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) # Set up the argument_names array _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "wrong") with pytest.raises(ValueError) as err: - intrinsic.canonicalise() - assert ("Found invalid argument name 'wrong' when canonicalising the " - "'SUM' IntrinsicCall. Allowed argument names are " + intrinsic.compute_argument_names() + assert ("Found invalid argument name 'wrong' when computing argument " + "names for the 'SUM' IntrinsicCall. Allowed argument names are " "'['array', 'dim', 'mask']'." in str(err.value)) - # Test canonicalisation fails if we don't have enough arguments. + # Test argument name computation fails if we don't have enough arguments. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) with pytest.raises(ValueError) as err: - intrinsic.canonicalise() - assert ("Found too few arguments when canonicalising the 'SUM' " - "IntrinsicCall. Requires at least 1 arguments but found 0." + intrinsic.compute_argument_names() + assert ("Found too few arguments when computing argument names for the " + "'SUM' IntrinsicCall. Requires at least 1 arguments but found 0." in str(err.value)) - # Test canonicalisation fails if we have too many arguments + # Test argument name computation fails if we have too many arguments intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) with pytest.raises(ValueError) as err: - intrinsic.canonicalise() - assert ("Found too many arguments when canonicalising the 'SUM' " - "IntrinsicCall. Requires at most 3 arguments but found 4." + intrinsic.compute_argument_names() + assert ("Found too many arguments when computing argument names for the " + "'SUM' IntrinsicCall. Requires at most 3 arguments but found 4." in str(err.value)) -def test_intrinsic_canonicalisation_not_implemented_errors(): +def test_intrinsic_compute_argument_names_not_implemented_errors(): ''' - Test the canonicalisation function of the IntrinsicCall class raises + Test the compute_argument_names function of the IntrinsicCall class raises NotImplementedErrors for Intrinsic structures PSyclone can't handle. ''' - # Test canonicalisation doesn't work when we have 2 arguments for SUM + # Test computing argument names doesn't work when we have 2 arguments for SUM # with no naming, as it can't determine between the SUM variants. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) with pytest.raises(NotImplementedError) as err: - intrinsic.canonicalise() - assert ("Cannot canonicalise 'SUM' IntrinsicCall as PSyclone can't " - "determine which argument set it should use. This can be " + intrinsic.compute_argument_names() + assert ("Cannot add argument names to 'SUM' IntrinsicCall as PSyclone " + "can't determine which argument set it should use. This can be " "resolved by using named arguments in the Fortran source." in str(err.value)) @@ -652,11 +653,11 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "n1") intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "n2") with pytest.raises(NotImplementedError) as err: - intrinsic.canonicalise() - assert ("Cannot canonicalise 'BESSEL_JN' IntrinsicCall as PSyclone can't " - "determine which argument set it should use. This can be resolved " - "by using named arguments in the Fortran source" - in str(err.value)) + intrinsic.compute_argument_names() + assert ("Cannot add argument names to 'BESSEL_JN' IntrinsicCall as " + "PSyclone can't determine which argument set it should use. " + "This can be resolved by using named arguments in the Fortran " + "source" in str(err.value)) # Test we get the expected error when non-optional argument names are # passed to an intrinsic where PSyclone can't handle the required argument @@ -666,47 +667,49 @@ def test_intrinsic_canonicalisation_not_implemented_errors(): _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "array") with pytest.raises(NotImplementedError) as err: - intrinsic.canonicalise() - assert ("Cannot canonicalise 'ALLOCATED' as non-optional argument name " - "'array' found but the Intrinsic has context-sensitive argument " - "names which is unsupported by PSyclone." in str(err.value)) + intrinsic.compute_argument_names() + assert ("Cannot add argument names to 'ALLOCATED' as non-optional " + "argument name 'array' found but the Intrinsic has " + "context-sensitive argument names which is unsupported by " + "PSyclone." in str(err.value)) -def test_canonicalisation(): +def test_compute_argument_names(): ''' - Test that the canonicalisation function works as expected for - cases that can be canonicalised. + Test that the compute_argument_names function works as expected for + cases that can have argument names computed. ''' - # Test canonicalisation works if we have 1 argument for SUM. + # Test argument name computation works if we have 1 argument for SUM. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names[0] == "array" - # Test canonicalisation does work when we give a name to the 2nd argument. + # Test argument name compuutation works when we give a name to the + # 2nd argument. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) # Set up the argument_names array and set the second ones name to be mask _ = intrinsic.argument_names intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "mask") - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == ["array", "mask"] - # Test that the correct canonicalisation is performed when we have a named - # argument only in one of the lists. + # Test that the correct argument name computation is performed when we + # have a named argument only in one of the lists. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) # Set up the argument_names array and set the second ones name to be dim _ = intrinsic.argument_names intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "dim") - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names[0] == "array" assert intrinsic.argument_names[1] == "dim" - # Test that canonicalisation works when optional arguments appear first - # when all arguments are named. + # Test that argument name computation works when optional arguments appear + # first when all arguments are named. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) a_arg = Reference(DataSymbol("a", INTEGER_TYPE)) b_arg = Reference(DataSymbol("b", INTEGER_TYPE)) @@ -717,19 +720,20 @@ def test_canonicalisation(): _ = intrinsic.argument_names intrinsic._argument_names[0] = (intrinsic._argument_names[0][0], "mask") intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "array") - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == ["mask", "array"] assert intrinsic.arguments[0] is a_arg assert intrinsic.arguments[1] is b_arg - # Check we can canonicalise an intrinsic with only one argument set. + # Check we can compute argument names for an intrinsic with only one + # argument set. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SIN) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == ["x"] - # Check that canonicalisation succeeds for an intrinsic with no required - # arguments + # Check that argument name computation succeeds for an intrinsic with no + # required arguments intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SYSTEM_CLOCK) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) @@ -740,21 +744,21 @@ def test_canonicalisation(): intrinsic._argument_names[1] = (intrinsic._argument_names[1][0], "count_max") intrinsic._argument_names[2] = (intrinsic._argument_names[2][0], "count") - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == ["count_rate", "count_max", "count"] assert intrinsic.children[1].symbol.name == "a" assert intrinsic.children[2].symbol.name == "b" assert intrinsic.children[3].symbol.name == "c" # Test canonicliation for intrinsic when PSyclone can't - # canonicalise the names of non-optional arguments. + # compute argument names of non-optional arguments. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATE) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) intrinsic.argument_names intrinsic._argument_names[2] = (intrinsic._argument_names[2][0], "mold") - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == [None, None, "mold"] # Check that we canoncalise when we have unnamed optional arguments. @@ -762,14 +766,14 @@ def test_canonicalisation(): intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("b", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("c", INTEGER_TYPE))) - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == ["array", "dim", "mask"] # Check that we don't fail when the required argument name is None # and no argument name can be generated by PSyclone. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATED) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) - intrinsic.canonicalise() + intrinsic.compute_argument_names() assert intrinsic.argument_names == [None] From fcc9892530a8ff1f72c8c99c47628c462e614297 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 12 Nov 2025 16:06:32 +0000 Subject: [PATCH 39/50] linting --- src/psyclone/tests/psyir/nodes/intrinsic_call_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index 478edfe499..e4cc549721 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -629,8 +629,8 @@ def test_intrinsic_compute_argument_names_not_implemented_errors(): Test the compute_argument_names function of the IntrinsicCall class raises NotImplementedErrors for Intrinsic structures PSyclone can't handle. ''' - # Test computing argument names doesn't work when we have 2 arguments for SUM - # with no naming, as it can't determine between the SUM variants. + # Test computing argument names doesn't work when we have 2 arguments for + # SUM with no naming, as it can't determine between the SUM variants. intrinsic = IntrinsicCall(IntrinsicCall.Intrinsic.SUM) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) intrinsic.addchild(Reference(DataSymbol("a", INTEGER_TYPE))) From a50235e0d394f939f6d0366b81e5586fd75768ac Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Tue, 18 Nov 2025 11:47:02 +0000 Subject: [PATCH 40/50] Doc update --- doc/developer_guide/psyir.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/developer_guide/psyir.rst b/doc/developer_guide/psyir.rst index 3f5571aef8..b8873694ae 100644 --- a/doc/developer_guide/psyir.rst +++ b/doc/developer_guide/psyir.rst @@ -599,11 +599,10 @@ within PSyclone during their creation (via the ``IntrinsicCall.create`` function). This attempts to match the Intrinsic and input arguments to one of the interfaces for the intrinsic (as some intrinsics have multiple possible argument interfaces). If the canonicalisation is successful, PSyclone -will convert all of the arguments to be named arguments, and reorder arguments -to match the specification from the Fortran standard. If canonicalisation -fails, then PSyclone will not create an ``IntrinsicCall`` corresponding to -the input. This canonicalisation is required to guarantee correct behaviour -when computing reference_accesses or the return type of an Intrinsic. +will convert all of the arguments to be named arguments. If canonicalisation +fails, then PSyclone will create a ``CodeBlock``. This canonicalisation is +required to guarantee correct behaviour when computing reference_accesses +or the return type of an Intrinsic. IntrinsicCalls, like Calls, have properties to inform if the call is to a pure, elemental, inquiry (does not touch the first argument data) function From 7ef9bdb287d3d1a92627e9b357acc9d10be848a8 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 20 Nov 2025 11:02:37 +0000 Subject: [PATCH 41/50] Changed default output and fixed tests --- src/psyclone/configuration.py | 2 +- src/psyclone/generator.py | 15 ++- src/psyclone/psyir/backend/sympy_writer.py | 20 ++- src/psyclone/psyir/nodes/intrinsic_call.py | 2 - src/psyclone/tests/configuration_test.py | 4 +- .../tests/core/symbolic_maths_test.py | 4 +- .../kernel_module_inline_trans_test.py | 2 +- ...ion_boundaries_inside_kernel_trans_test.py | 12 +- .../gocean_opencl_trans_test.py | 84 ++++++------- .../tests/domain/lfric/lfric_builtins_test.py | 22 ++-- .../nemo/transformations/acc_update_test.py | 4 +- src/psyclone/tests/generator_test.py | 4 +- src/psyclone/tests/gocean1p0_test.py | 4 +- .../tests/psyad/adjoint_visitor_test.py | 8 +- .../psyad/domain/common/test_adjoint_utils.py | 8 +- .../lfric/test_lfric_adjoint_harness.py | 10 +- src/psyclone/tests/psyad/main_test.py | 4 +- src/psyclone/tests/psyad/tl2ad_test.py | 18 +-- .../transformations/test_assignment_trans.py | 4 +- .../psyad/transformations/test_preprocess.py | 10 +- .../psyir/backend/fortran_gen_decls_test.py | 2 +- .../tests/psyir/backend/fortran_test.py | 53 ++++---- .../fparser2_intrinsic_handler_test.py | 4 +- .../tests/psyir/frontend/fparser2_test.py | 2 +- .../frontend/fparser2_where_handler_test.py | 56 ++++----- .../tests/psyir/nodes/array_mixin_test.py | 24 ++-- .../nodes/dynamic_omp_task_directive_test.py | 12 +- .../tests/psyir/nodes/intrinsic_call_test.py | 18 +-- .../nodes/type_convert_intrinsic_test.py | 6 +- .../transformations/acc_kernels_trans_test.py | 28 ++--- .../arrayassignment2loops_trans_test.py | 118 +++++++++--------- .../hoist_local_arrays_trans_test.py | 38 +++--- .../hoist_loop_bound_expr_trans_test.py | 8 +- .../psyir/transformations/hoist_trans_test.py | 2 +- .../intrinsics/abs2code_trans_test.py | 6 +- .../array_reduction_base_trans_test.py | 72 +++++------ .../intrinsics/dotproduct2code_trans_test.py | 28 ++--- .../intrinsics/matmul2code_trans_test.py | 4 +- .../intrinsics/maxval2loop_trans_test.py | 4 +- .../intrinsics/minval2loop_trans_test.py | 4 +- .../intrinsics/sign2code_trans_test.py | 10 +- .../omp_task_transformations_test.py | 4 +- .../reference2arrayrange_trans_test.py | 4 +- .../replace_induction_variables_trans_test.py | 16 +-- .../scalarisation_trans_test.py | 10 +- 45 files changed, 394 insertions(+), 380 deletions(-) diff --git a/src/psyclone/configuration.py b/src/psyclone/configuration.py index 264e4f08c6..cd9a0793d8 100644 --- a/src/psyclone/configuration.py +++ b/src/psyclone/configuration.py @@ -237,7 +237,7 @@ def __init__(self): # By default, the PSyIR backends output argument names on (most) # IntrinsicCalls. This option enables control of that behaviour. - self._backend_intrinsic_named_kwargs = True + self._backend_intrinsic_named_kwargs = False # ------------------------------------------------------------------------- def load(self, config_file=None): diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index c06eb66c05..5b717c93a2 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -589,14 +589,13 @@ def main(arguments): help="Disables all indentation in the generated output code." ) backend_group.add_argument( - "--backend-omit-unneeded-intrinsic-arg-names", + "--backend-add-all-intrinsic-arg-names", default=argparse.SUPPRESS, action="store_true", - help="By default, the backend names any required arguments to " - "intrinsic calls. This option disables this feature (in case " - "the processed code has overridden a Fortran intrinsic), " - "i.e. SUM(arr, mask=maskarr) instead of SUM(array=arr, " - "mask=maskarr)." + help="By default, the backend outputs the names of only optional " + "arguments to intrinsic calls. This option enables all argument " + "names on intrinsic calls, i.e. SUM(array=arr, mask=maskarr) " + "instead of SUM(arr, mask=maskarr)." ) args = parser.parse_args(arguments) @@ -651,10 +650,10 @@ def main(arguments): Config.get().api = api # Record any intrinsic output format settings. - if "backend_omit_unneeded_intrinsic_arg_names" in args: + if "backend_add_all_intrinsic_arg_names" in args: # The backend won't attempt to add names to required # arguments to Fortran intrinsics. - Config.get().backend_intrinsic_named_kwargs = False + Config.get().backend_intrinsic_named_kwargs = True # Record any profiling options. if args.profile: diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index 321bc5b9d8..b9292c9cab 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -53,7 +53,7 @@ from psyclone.psyir.nodes import ( ArrayOfStructuresReference, ArrayReference, BinaryOperation, Call, DataNode, IntrinsicCall, Literal, Node, - Range, Reference, StructureReference) + Range, Reference, StructureReference, Schedule) from psyclone.psyir.symbols import ( ArrayType, DataSymbol, RoutineSymbol, ScalarType, Symbol, SymbolError, SymbolTable, UnresolvedType) @@ -619,6 +619,7 @@ def __call__( result = [] for expr in expression_str_list: + print(expr) try: result.append(parse_expr(expr, self.type_map)) except SyntaxError as err: @@ -789,7 +790,22 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: args = self._gen_arguments(node) return f"{self._nindent}{name}({args})" except KeyError: - return super().intrinsiccall_node(node) + # This section is copied from FortranWriter IntrinsicCall, + # but doesn't attempt to match argument names and so avoids + # readding optional argument names back in. + args = self._gen_arguments(node) + if node.routine.name not in [ + "DATE_AND_TIME", "SYSTEM_CLOCK", "MVBITS", + "RANDOM_NUMBER", "RANDOM_SEED"]: + # Most intrinsics are functions and so don't have 'call'. + if not node.parent or isinstance(node.parent, Schedule): + return f"{self._nindent}{node.routine.name}({args})\n" + return f"{node.routine.name}({args})" + if not node.parent or isinstance(node.parent, Schedule): + return f"{self._nindent}call {self._visit(node.routine)}({args})\n" + + # Otherwise it is inside-expression function call + return f"{self._visit(node.routine)}({args})" # ------------------------------------------------------------------------- def reference_node(self, node: Reference) -> str: diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index 809846691c..d5c3420c66 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -3394,8 +3394,6 @@ def compute_argument_names(self): self._argument_names[i] = (self._argument_names[i][0], optional_names[i - len( interface_arg_names)]) - if self.intrinsic.name == "MAX": - print(self.argument_names) @classmethod def create(cls, intrinsic, arguments=()): diff --git a/src/psyclone/tests/configuration_test.py b/src/psyclone/tests/configuration_test.py index efaee81b49..5405e7ed7d 100644 --- a/src/psyclone/tests/configuration_test.py +++ b/src/psyclone/tests/configuration_test.py @@ -824,9 +824,9 @@ def test_fortran_standard(tmpdir): def test_intrinsic_settings(): '''Test the getter and setter methods for controlling the output of named arguments on intrinsics in the config.''' - assert Config.get().backend_intrinsic_named_kwargs is True - Config.get().backend_intrinsic_named_kwargs = False assert Config.get().backend_intrinsic_named_kwargs is False + Config.get().backend_intrinsic_named_kwargs = True + assert Config.get().backend_intrinsic_named_kwargs is True with pytest.raises(TypeError) as err: Config.get().backend_intrinsic_named_kwargs = 1 diff --git a/src/psyclone/tests/core/symbolic_maths_test.py b/src/psyclone/tests/core/symbolic_maths_test.py index 5353865da4..4b8fe9f50a 100644 --- a/src/psyclone/tests/core/symbolic_maths_test.py +++ b/src/psyclone/tests/core/symbolic_maths_test.py @@ -480,7 +480,7 @@ def test_symbolic_math_use_range(fortran_reader, expressions): # not it is a scalar. ("a / a(i)", "a / a(i)"), ("norm_u(idx+iw2) * u_e(idx + (LBOUND(u_e,dim=1)-iw2v), df2)", - "norm_u(idx + iw2) * u_e(idx - iw2v + LBOUND(array=u_e, dim=1),df2)"), + "norm_u(idx + iw2) * u_e(idx - iw2v + LBOUND(u_e, dim=1),df2)"), (".true. .and. .false.", ".false."), ("zh_cum1(jk1) <= zh_cum0(jk0) .AND. zh_cum1(jk1) > zh_cum0(jk0 - 1)", "zh_cum0(jk0) >= zh_cum1(jk1) .AND. zh_cum1(jk1) > zh_cum0(jk0 - 1)"), @@ -533,7 +533,7 @@ def test_expand_with_intrinsic(fortran_reader, fortran_writer): sym_maths.expand(rhs) result = fortran_writer(psyir).lower() # Check that the 'u_e' argument remains unchanged. - assert "lbound(array=u_e, dim=1),df2)" in result + assert "lbound(u_e, dim=1),df2)" in result def test_symbolic_maths_expand_function(fortran_reader, fortran_writer): diff --git a/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py b/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py index e33452042b..90f597f9e8 100644 --- a/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py +++ b/src/psyclone/tests/domain/common/transformations/kernel_module_inline_trans_test.py @@ -94,7 +94,7 @@ def test_validate_inline_error_if_not_kernel(fortran_reader): call = psyir.walk(Call)[0] with pytest.raises(TransformationError) as err: inline_trans.apply(call) - assert ("Cannot module-inline a call to an intrinsic (got 'SIN(x=b)')" + assert ("Cannot module-inline a call to an intrinsic (got 'SIN(b)')" in str(err.value)) diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean_move_iteration_boundaries_inside_kernel_trans_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean_move_iteration_boundaries_inside_kernel_trans_test.py index d49610add6..25b643d11b 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean_move_iteration_boundaries_inside_kernel_trans_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean_move_iteration_boundaries_inside_kernel_trans_test.py @@ -195,18 +195,18 @@ def test_go_move_iteration_boundaries_inside_kernel_two_kernels_apply_twice( xstop = cu_fld%internal%xstop ystart = cu_fld%internal%ystart ystop = cu_fld%internal%ystop - do j = 1, SIZE(cu_fld%data, 2), 1 - do i = 1, SIZE(cu_fld%data, 1), 1 + do j = 1, SIZE(cu_fld%data, dim=2), 1 + do i = 1, SIZE(cu_fld%data, dim=1), 1 call compute_cu_code(i, j, cu_fld%data, p_fld%data, u_fld%data, xstart, \ xstop, ystart, ystop) enddo enddo xstart_1 = 1 - xstop_1 = SIZE(uold_fld%data, 1) + xstop_1 = SIZE(uold_fld%data, dim=1) ystart_1 = 1 - ystop_1 = SIZE(uold_fld%data, 2) - do j = 1, SIZE(uold_fld%data, 2), 1 - do i = 1, SIZE(uold_fld%data, 1), 1 + ystop_1 = SIZE(uold_fld%data, dim=2) + do j = 1, SIZE(uold_fld%data, dim=2), 1 + do i = 1, SIZE(uold_fld%data, dim=1), 1 call time_smooth_code(i, j, u_fld%data, unew_fld%data, uold_fld%data, \ xstart_1, xstop_1, ystart_1, ystop_1) enddo diff --git a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py index 8968645f48..7d416d95e8 100644 --- a/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py +++ b/src/psyclone/tests/domain/gocean/transformations/gocean_opencl_trans_test.py @@ -260,9 +260,9 @@ def test_invoke_opencl_initialisation(kernel_outputdir, fortran_writer): call initialise_device_buffer(u_fld) ! do a set_args now so subsequent writes place the data appropriately - cu_fld_cl_mem = transfer(source=cu_fld%device_ptr, mold=cu_fld_cl_mem) - p_fld_cl_mem = transfer(source=p_fld%device_ptr, mold=p_fld_cl_mem) - u_fld_cl_mem = transfer(source=u_fld%device_ptr, mold=u_fld_cl_mem) + cu_fld_cl_mem = transfer(cu_fld%device_ptr, cu_fld_cl_mem) + p_fld_cl_mem = transfer(p_fld%device_ptr, p_fld_cl_mem) + u_fld_cl_mem = transfer(u_fld%device_ptr, u_fld_cl_mem) call compute_cu_code_set_args(kernel_compute_cu_code, cu_fld_cl_mem, \ p_fld_cl_mem, u_fld_cl_mem, xstart - 1, xstop - 1, ystart - 1, ystop - 1) @@ -316,18 +316,18 @@ def test_invoke_opencl_initialisation_grid(): integer(kind=c_size_t) :: size_in_bytes if (.not.c_associated(field%grid%tmask_device)) then - size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ + size_in_bytes = int(field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%tmask(1,1)) - field%grid%tmask_device = transfer(source=create_ronly_buffer(\ -size_in_bytes), mold=field%grid%tmask_device) - size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ + field%grid%tmask_device = transfer(create_ronly_buffer(\ +size_in_bytes), field%grid%tmask_device) + size_in_bytes = int(field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%''' assert expected in generated_code for grid_property in check_properties: code = (f"field%grid%{grid_property}_device = transfer(" - f"source=create_ronly_buffer(size_in_bytes), " - f"mold=field%grid%{grid_property}_device)") + f"create_ronly_buffer(size_in_bytes), " + f"field%grid%{grid_property}_device)") assert code in generated_code # Check that device grid write routine is generated @@ -344,19 +344,19 @@ def test_invoke_opencl_initialisation_grid(): integer :: ierr cmd_queues => get_cmd_queues() - size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ + size_in_bytes = int(field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%tmask(1,1)) - cl_mem = transfer(source=field%grid%tmask_device, mold=cl_mem) + cl_mem = transfer(field%grid%tmask_device, cl_mem) ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem,cl_true,0_8,\ size_in_bytes,c_loc(field%grid%tmask),0,c_null_ptr,c_null_ptr) call check_status('clenqueuewritebuffer tmask', ierr) - size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ + size_in_bytes = int(field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%grid%area_t(1,1))''' assert expected in generated_code for grid_property in check_properties: - code = (f" cl_mem = transfer(source=field%grid%{grid_property}_" - f"device, mold=cl_mem)\n" + code = (f" cl_mem = transfer(field%grid%{grid_property}_" + f"device, cl_mem)\n" f" ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem," f"cl_true,0_8,size_in_bytes,c_loc(field%grid%{grid_property})," f"0,c_null_ptr,c_null_ptr)\n" @@ -384,13 +384,13 @@ def test_invoke_opencl_initialisation_grid(): call initialise_grid_device_buffers(in_fld) ! do a set_args now so subsequent writes place the data appropriately - out_fld_cl_mem = transfer(source=out_fld%device_ptr, mold=out_fld_cl_mem) - in_out_fld_cl_mem = transfer(source=in_out_fld%device_ptr, \ -mold=in_out_fld_cl_mem) - in_fld_cl_mem = transfer(source=in_fld%device_ptr, mold=in_fld_cl_mem) - dx_cl_mem = transfer(source=dx%device_ptr, mold=dx_cl_mem) - gphiu_cl_mem = transfer(source=in_fld%grid%gphiu_device, \ -mold=gphiu_cl_mem) + out_fld_cl_mem = transfer(out_fld%device_ptr, out_fld_cl_mem) + in_out_fld_cl_mem = transfer(in_out_fld%device_ptr, \ +in_out_fld_cl_mem) + in_fld_cl_mem = transfer(in_fld%device_ptr, in_fld_cl_mem) + dx_cl_mem = transfer(dx%device_ptr, dx_cl_mem) + gphiu_cl_mem = transfer(in_fld%grid%gphiu_device, \ +gphiu_cl_mem) call compute_kernel_code_set_args(kernel_compute_kernel_code, \ out_fld_cl_mem, in_out_fld_cl_mem, in_fld_cl_mem, dx_cl_mem, \ in_fld%grid%dx, gphiu_cl_mem, xstart - 1, xstop - 1, ystart - 1, \ @@ -450,12 +450,12 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer :: ierr integer :: i - cl_mem = transfer(source=from, mold=cl_mem) + cl_mem = transfer(from, cl_mem) cmd_queues => get_cmd_queues() - if (nx < size(array=to, dim=1) / 2) then + if (nx < size(to, dim=1) / 2) then do i = starty, starty + ny, 1 - size_in_bytes = int(a=nx, kind=8) * c_sizeof(to(1,1)) - offset_in_bytes = int(a=size(array=to, dim=1) * (i - 1) + \ + size_in_bytes = int(nx, kind=8) * c_sizeof(to(1,1)) + offset_in_bytes = int(size(to, dim=1) * (i - 1) + \ (startx - 1)) * c_sizeof(to(1,1)) ierr = clenqueuereadbuffer(cmd_queues(1),cl_mem,cl_false,\ offset_in_bytes,size_in_bytes,c_loc(to(startx,i)),0,c_null_ptr,c_null_ptr) @@ -465,9 +465,9 @@ def test_opencl_routines_initialisation(kernel_outputdir): call check_status('clfinish on read', clfinish(cmd_queues(1))) end if else - size_in_bytes = int(a=size(array=to, dim=1) * ny, kind=8) * \ + size_in_bytes = int(size(to, dim=1) * ny, kind=8) * \ c_sizeof(to(1,1)) - offset_in_bytes = int(a=size(array=to, dim=1) * (starty - 1), kind=8) \ + offset_in_bytes = int(size(to, dim=1) * (starty - 1), kind=8) \ * c_sizeof(to(1,1)) ierr = clenqueuereadbuffer(cmd_queues(1),cl_mem,cl_true,\ offset_in_bytes,size_in_bytes,c_loc(to(1,starty)),0,c_null_ptr,c_null_ptr) @@ -499,12 +499,12 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer :: ierr integer :: i - cl_mem = transfer(source=to, mold=cl_mem) + cl_mem = transfer(to, cl_mem) cmd_queues => get_cmd_queues() - if (nx < size(array=from, dim=1) / 2) then + if (nx < size(from, dim=1) / 2) then do i = starty, starty + ny, 1 - size_in_bytes = int(a=nx, kind=8) * c_sizeof(from(1,1)) - offset_in_bytes = int(a=size(array=from, dim=1) * (i - 1) + \ + size_in_bytes = int(nx, kind=8) * c_sizeof(from(1,1)) + offset_in_bytes = int(size(from, dim=1) * (i - 1) + \ (startx - 1)) * c_sizeof(from(1,1)) ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem,cl_false,\ offset_in_bytes,size_in_bytes,c_loc(from(startx,i)),0,c_null_ptr,c_null_ptr) @@ -514,9 +514,9 @@ def test_opencl_routines_initialisation(kernel_outputdir): call check_status('clfinish on write', clfinish(cmd_queues(1))) end if else - size_in_bytes = int(a=size(array=from, dim=1) * ny, kind=8) * \ + size_in_bytes = int(size(from, dim=1) * ny, kind=8) * \ c_sizeof(from(1,1)) - offset_in_bytes = int(a=size(array=from, dim=1) * (starty - 1)) * \ + offset_in_bytes = int(size(from, dim=1) * (starty - 1)) * \ c_sizeof(from(1,1)) ierr = clenqueuewritebuffer(cmd_queues(1),cl_mem,cl_true,\ offset_in_bytes,size_in_bytes,c_loc(from(1,starty)),0,c_null_ptr,c_null_ptr) @@ -536,10 +536,10 @@ def test_opencl_routines_initialisation(kernel_outputdir): integer(kind=c_size_t) :: size_in_bytes if (.not.field%data_on_device) then - size_in_bytes = int(a=field%grid%nx * field%grid%ny, kind=8) * \ + size_in_bytes = int(field%grid%nx * field%grid%ny, kind=8) * \ c_sizeof(field%data(1,1)) - field%device_ptr = transfer(source=create_rw_buffer(size_in_bytes), \ -mold=field%device_ptr) + field%device_ptr = transfer(create_rw_buffer(size_in_bytes), \ +field%device_ptr) field%data_on_device = .true. field%read_from_device_f => read_from_device field%write_to_device_f => write_to_device @@ -651,7 +651,7 @@ def test_psy_init_multiple_devices_per_node(kernel_outputdir, monkeypatch): if (.not.initialised) then initialised = .true. - ocl_device_num = mod(a=get_rank() - 1, p=2) + 1 + ocl_device_num = mod(get_rank() - 1, 2) + 1 call ocl_env_init(1, ocl_device_num, .false., .false.) kernel_names(1) = 'compute_cu_code' call add_kernels(1, kernel_names) @@ -711,7 +711,7 @@ def test_invoke_opencl_kernel_call(kernel_outputdir, monkeypatch, debug_mode): # Check that the globalsize first dimension is a multiple of # the localsize first dimension expected += ''' - if (MOD(a=p_fld%grid%nx, p=64) /= 0) then + if (MOD(p_fld%grid%nx, 64) /= 0) then call check_status('Global size is not a multiple of local size \ (mandatory in OpenCL < 2.0).', -1) end if''' @@ -725,9 +725,9 @@ def test_invoke_opencl_kernel_call(kernel_outputdir, monkeypatch, debug_mode): # Cast dl_esm_inf pointers to cl_mem handlers expected += ''' - cu_fld_cl_mem = TRANSFER(source=cu_fld%device_ptr, mold=cu_fld_cl_mem) - p_fld_cl_mem = TRANSFER(source=p_fld%device_ptr, mold=p_fld_cl_mem) - u_fld_cl_mem = TRANSFER(source=u_fld%device_ptr, mold=u_fld_cl_mem)''' + cu_fld_cl_mem = TRANSFER(cu_fld%device_ptr, cu_fld_cl_mem) + p_fld_cl_mem = TRANSFER(p_fld%device_ptr, p_fld_cl_mem) + u_fld_cl_mem = TRANSFER(u_fld%device_ptr, u_fld_cl_mem)''' # Call the set_args subroutine with the boundaries corrected for the # OpenCL 0-indexing @@ -967,7 +967,7 @@ def test_multiple_command_queues(dist_mem): kernelbarrier = ''' ierr = clFinish(cmd_queues(2)) - p_fld_cl_mem = TRANSFER(source=p_fld%device_ptr, mold=p_fld_cl_mem)''' + p_fld_cl_mem = TRANSFER(p_fld%device_ptr, p_fld_cl_mem)''' haloexbarrier = ''' ierr = clFinish(cmd_queues(2)) diff --git a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py index c1436687c9..a04a46a4da 100644 --- a/src/psyclone/tests/domain/lfric/lfric_builtins_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_builtins_test.py @@ -1638,7 +1638,7 @@ def test_setval_random(fortran_writer, annexed): code = fortran_writer(kern) assert ("! Built-in: setval_random (fill a real-valued field " "with pseudo-random numbers)\n" - "call RANDOM_NUMBER(harvest=f1_data(df))\n") in code + "call RANDOM_NUMBER(f1_data(df))\n") in code # ------------- Sign of real field elements --------------------------------- # @@ -1664,7 +1664,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(a=a, b=f1_data(df))\n") in code + "f2_data(df) = SIGN(a, f1_data(df))\n") in code # Also with a literal kern = builtin_from_file("15.10.2_sign_X_builtin_set_by_value.f90") @@ -1675,7 +1675,7 @@ def test_sign_X_and_its_int_version(fortran_writer): code = fortran_writer(kern) assert ("! Built-in: sign_X (sign of a real-valued field, applied to a " "scalar argument)\n" - "f2_data(df) = SIGN(a=-2.0_r_def, b=f1_data(df))\n" in code) + "f2_data(df) = SIGN(-2.0_r_def, f1_data(df))\n" in code) # The integer version has the datatype changed to integer in the metadata # and string representation @@ -1927,7 +1927,7 @@ def test_int_to_real_x(fortran_writer): assert ( "! Built-in: int_to_real_X (convert an integer-valued to a " "real-valued field)\n" - "f2_data(df) = REAL(a=f1_data(df), kind=r_def)\n") in code + "f2_data(df) = REAL(f1_data(df), kind=r_def)\n") in code @pytest.mark.parametrize("kind_name", ["r_solver", "r_tran", "r_bl"]) @@ -1969,7 +1969,7 @@ def test_int_to_real_x_precision(tmpdir, kind_name): assert (f"real(kind={kind_name}), pointer, dimension(:) :: " "f2_data => null()") in code assert f"type({kind_name}_field_proxy_type) :: f2_proxy" in code - assert f"f2_data(df) = REAL(a=f1_data(df), kind={kind_name})" in code + assert f"f2_data(df) = REAL(f1_data(df), kind={kind_name})" in code # Test compilation of generated code assert LFRicBuild(tmpdir).code_compiles(psy) @@ -1996,7 +1996,7 @@ def test_real_to_int_x(fortran_writer): assert ( "! Built-in: real_to_int_X (convert a real-valued to an " "integer-valued field)\n" - "f2_data(df) = INT(a=f1_data(df), kind=i_def)\n") in code + "f2_data(df) = INT(f1_data(df), kind=i_def)\n") in code @pytest.mark.parametrize("kind_name", ["i_um", "i_ncdf"]) @@ -2027,7 +2027,7 @@ def test_real_to_int_x_precision(monkeypatch, tmpdir, kind_name): assert "use constants_mod\n" in code assert ("integer(kind=i_def), pointer, dimension(:) :: f2_data => null()" in code) - assert f"f2_data(df) = INT(a=f1_data(df), kind={kind_name})" in code + assert f"f2_data(df) = INT(f1_data(df), kind={kind_name})" in code # Test compilation of generated code assert LFRicBuild(tmpdir).code_compiles(psy) @@ -2058,17 +2058,17 @@ def test_real_to_real_x(fortran_writer): assert ( "! Built-in: real_to_real_X (convert a real-valued to a " "real-valued field)\n" - "f2_data(df) = REAL(a=f1_data(df), kind=r_tran)\n") in code + "f2_data(df) = REAL(f1_data(df), kind=r_tran)\n") in code elif idx == 1: assert ( "! Built-in: real_to_real_X (convert a real-valued to a " "real-valued field)\n" - "f1_data(df) = REAL(a=f3_data(df), kind=r_def)\n") in code + "f1_data(df) = REAL(f3_data(df), kind=r_def)\n") in code elif idx == 2: assert ( "! Built-in: real_to_real_X (convert a real-valued to a " "real-valued field)\n" - "f3_data(df) = REAL(a=f2_data(df), kind=r_solver)\n") in code + "f3_data(df) = REAL(f2_data(df), kind=r_solver)\n") in code else: assert False, code # There are only 3 kern @@ -2099,7 +2099,7 @@ def test_real_to_real_x_lowering(monkeypatch, tmpdir, kind_name): assert "use constants_mod\n" in code # Assert correct type is set - assert f"f2_data(df) = REAL(a=f1_data(df), kind={kind_name})" in code + assert f"f2_data(df) = REAL(f1_data(df), kind={kind_name})" in code # Test compilation of generated code assert LFRicBuild(tmpdir).code_compiles(psy) diff --git a/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py b/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py index 2404799798..bc6f6cc843 100644 --- a/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py +++ b/src/psyclone/tests/domain/nemo/transformations/acc_update_test.py @@ -215,7 +215,7 @@ def test_call_accesses(fortran_reader, fortran_writer): " !$acc update if_present device(zftv)\n" " call dia_ptr_hst(jn, 'ldf', zftv(:,:,:))\n" " !$acc update if_present host(checksum,zftv)\n" - " checksum = SUM(array=zftv)\n" + " checksum = SUM(zftv)\n" " !$acc update if_present device(checksum,jn,zftv)\n" in code) @@ -247,7 +247,7 @@ def test_call_within_if(fortran_reader, fortran_writer): " !$acc update if_present device(zftv)\n" in code) assert (" end if\n" " !$acc update if_present host(checksum,zftv)\n" - " checksum = SUM(array=zftv)\n" + " checksum = SUM(zftv)\n" " !$acc update if_present device(checksum)\n" in code) diff --git a/src/psyclone/tests/generator_test.py b/src/psyclone/tests/generator_test.py index d3cc485898..fbd8258f28 100644 --- a/src/psyclone/tests/generator_test.py +++ b/src/psyclone/tests/generator_test.py @@ -2054,5 +2054,5 @@ def test_intrinsic_control_settings(tmpdir, caplog): filename = str(tmpdir.join("test.f90")) with open(filename, "w", encoding='utf-8') as my_file: my_file.write(code) - main([filename, "--backend-omit-unneeded-intrinsic-arg-names"]) - assert Config.get().backend_intrinsic_named_kwargs is False + main([filename, "--backend-add-all-intrinsic-arg-names"]) + assert Config.get().backend_intrinsic_named_kwargs is True diff --git a/src/psyclone/tests/gocean1p0_test.py b/src/psyclone/tests/gocean1p0_test.py index fc6bc75bd0..467331815d 100644 --- a/src/psyclone/tests/gocean1p0_test.py +++ b/src/psyclone/tests/gocean1p0_test.py @@ -153,8 +153,8 @@ def test_two_kernels(tmpdir, dist_mem): " enddo\n" " enddo\n") second_kernel = ( - " do j = 1, SIZE(uold_fld%data, 2), 1\n" - " do i = 1, SIZE(uold_fld%data, 1), 1\n" + " do j = 1, SIZE(uold_fld%data, dim=2), 1\n" + " do i = 1, SIZE(uold_fld%data, dim=1), 1\n" " call time_smooth_code(i, j, u_fld%data, unew_fld%data, " "uold_fld%data)\n" " enddo\n" diff --git a/src/psyclone/tests/psyad/adjoint_visitor_test.py b/src/psyclone/tests/psyad/adjoint_visitor_test.py index 5fd031debb..ff4c882739 100644 --- a/src/psyclone/tests/psyad/adjoint_visitor_test.py +++ b/src/psyclone/tests/psyad/adjoint_visitor_test.py @@ -640,9 +640,9 @@ def test_loop_node_bounds_intrinsic(fortran_reader, fortran_writer, tmpdir): " integer :: i\n\n" " a = 0.0\n" " b = 0.0\n" - " do i = UBOUND(array=a, dim=1) - MOD(UBOUND(array = a, dim = 1) " - "- LBOUND(array = a, dim = 1), 2 * UBOUND(array = b, dim = 1)), " - "LBOUND(array=a, dim=1), -1 * (2 * UBOUND(array=b, dim=1))\n" + " do i = UBOUND(a, dim=1) - MOD(UBOUND(a, dim = 1) " + "- LBOUND(a, dim = 1), 2 * UBOUND(b, dim = 1)), " + "LBOUND(a, dim=1), -1 * (2 * UBOUND(b, dim=1))\n" " a(i) = 0.0\n" " enddo\n\n" "end program test\n") @@ -667,7 +667,7 @@ def test_loop_node_bounds_intrinsic(fortran_reader, fortran_writer, tmpdir): with pytest.raises(VisitorError) as error: _ = adj_visitor(tl_psyir) assert ("The upper bound of a loop should not contain active variables, " - "but found 'b' in 'UBOUND(array=a, dim=b)'." in str(error.value)) + "but found 'b' in 'UBOUND(a, dim=b)'." in str(error.value)) def test_loop_node_passive(fortran_reader): diff --git a/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py b/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py index 38168bbd61..51dda378fc 100644 --- a/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py +++ b/src/psyclone/tests/psyad/domain/common/test_adjoint_utils.py @@ -87,8 +87,8 @@ def test_create_real_comparison(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" - " relative_diff = ABS(a=var1 - var2) / MachineTol\n" + " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" + " relative_diff = ABS(var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" " ! - Unsupported statement: Write_Stmt\n" @@ -125,8 +125,8 @@ def test_common_real_comparison(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" - " relative_diff = ABS(a=var1 - var2) / MachineTol\n") + " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" + " relative_diff = ABS(var1 - var2) / MachineTol\n") assert expected in result # _common_write diff --git a/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py b/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py index 283f8da3d6..8279e1f764 100644 --- a/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py +++ b/src/psyclone/tests/psyad/domain/lfric/test_lfric_adjoint_harness.py @@ -458,8 +458,8 @@ def test_lfric_create_real_comparison(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" - " relative_diff = ABS(a=var1 - var2) / MachineTol\n" + " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" + " relative_diff = ABS(var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" " ! - Unsupported statement: Write_Stmt\n" @@ -500,8 +500,8 @@ def test_lfric_log_write(fortran_writer): " real :: relative_diff\n\n" " ! Test the inner-product values for equality, allowing for the " "precision of the active variables\n" - " MachineTol = SPACING(x=MAX(ABS(a=var1), ABS(a=var2)))\n" - " relative_diff = ABS(a=var1 - var2) / MachineTol\n" + " MachineTol = SPACING(MAX(ABS(var1), ABS(var2)))\n" + " relative_diff = ABS(var1 - var2) / MachineTol\n" " if (relative_diff < overall_tolerance) then\n" " ! PSyclone CodeBlock (unsupported code) reason:\n" " ! - Unsupported statement: Write_Stmt\n" @@ -595,7 +595,7 @@ def test_generate_lfric_adjoint_harness(fortran_reader, fortran_writer): assert ("call field_input%initialise(vector_space=vector_space_w3_ptr," " name='field_input')" in gen) # So too must the scalar argument. - assert (" call random_number(harvest=ascalar)\n" + assert (" call random_number(ascalar)\n" " ascalar_input = ascalar\n" in gen) # The field must be given random values and those copied into the copy. diff --git a/src/psyclone/tests/psyad/main_test.py b/src/psyclone/tests/psyad/main_test.py index 2618998479..68fc4686de 100644 --- a/src/psyclone/tests/psyad/main_test.py +++ b/src/psyclone/tests/psyad/main_test.py @@ -150,8 +150,8 @@ ! test the inner-product values for equality, allowing for the precision \ of the active variables - machinetol = spacing(x=max(abs(a=inner1), abs(a=inner2))) - relative_diff = abs(a=inner1 - inner2) / machinetol + machinetol = spacing(max(abs(inner1), abs(inner2))) + relative_diff = abs(inner1 - inner2) / machinetol if (relative_diff < overall_tolerance) then ! psyclone codeblock (unsupported code) reason: ! - unsupported statement: write_stmt diff --git a/src/psyclone/tests/psyad/tl2ad_test.py b/src/psyclone/tests/psyad/tl2ad_test.py index e0174c5ba3..9e472da77a 100644 --- a/src/psyclone/tests/psyad/tl2ad_test.py +++ b/src/psyclone/tests/psyad/tl2ad_test.py @@ -710,8 +710,8 @@ def test_generate_adjoint_test(fortran_reader, fortran_writer): " ! compute the inner product of the results of the tangent-" "linear kernel\n" " inner1 = 0.0\n" - " inner1 = inner1 + dot_product(vector_a=field, " - "vector_b=field)\n" + " inner1 = inner1 + dot_product(field, " + "field)\n" "\n" " ! call the adjoint of the kernel\n" " call adj_kern(field, npts)\n" @@ -719,12 +719,12 @@ def test_generate_adjoint_test(fortran_reader, fortran_writer): " ! compute inner product of results of adjoint kernel with " "the original inputs to the tangent-linear kernel\n" " inner2 = 0.0\n" - " inner2 = inner2 + dot_product(vector_a=field, " - "vector_b=field_input)\n" + " inner2 = inner2 + dot_product(field, " + "field_input)\n" "\n" " ! test the inner-product values for equality, allowing for " "the precision of the active variables\n" - " machinetol = spacing(x=max(abs(a=inner1), abs(a=inner2)))\n" + " machinetol = spacing(max(abs(inner1), abs(inner2)))\n" in harness.lower()) # Ideally we would test that the generated harness code compiles # but, since it depends on the TL and adjoint kernels, we can't @@ -1154,7 +1154,7 @@ def test_create_inner_product_1d_arrays(fortran_writer): assert isinstance(nodes[1].rhs, BinaryOperation) assert nodes[1].rhs.operator == BinaryOperation.Operator.ADD code = fortran_writer(nodes[1]) - assert ("result = result + DOT_PRODUCT(vector_a=var1, vector_b=var2)" + assert ("result = result + DOT_PRODUCT(var1, var2)" in code) @@ -1179,7 +1179,7 @@ def test_create_inner_product_arrays(fortran_writer): assert isinstance(nodes[1].rhs, BinaryOperation) assert nodes[1].rhs.operator == BinaryOperation.Operator.ADD code = fortran_writer(nodes[1]) - assert "result = result + SUM(array=var1(:,:,:) * var2(:,:,:))" in code + assert "result = result + SUM(var1(:,:,:) * var2(:,:,:))" in code def test_inner_product_scalars_and_arrays(fortran_writer): @@ -1204,7 +1204,7 @@ def test_inner_product_scalars_and_arrays(fortran_writer): assert all(isinstance(node, Assignment) for node in nodes) assert fortran_writer(nodes[0]) == "result = 0.0\n" assert (fortran_writer(nodes[1]) == - "result = result + SUM(array=var1(:,:,:) * var2(:,:,:))\n") + "result = result + SUM(var1(:,:,:) * var2(:,:,:))\n") assert (fortran_writer(nodes[2]) == - "result = result + DOT_PRODUCT(vector_a=vec1, vector_b=vec2)\n") + "result = result + DOT_PRODUCT(vec1, vec2)\n") assert fortran_writer(nodes[3]) == "result = result + a1 * a2\n" diff --git a/src/psyclone/tests/psyad/transformations/test_assignment_trans.py b/src/psyclone/tests/psyad/transformations/test_assignment_trans.py index 0237dd73f5..6a024b39fa 100644 --- a/src/psyclone/tests/psyad/transformations/test_assignment_trans.py +++ b/src/psyclone/tests/psyad/transformations/test_assignment_trans.py @@ -1282,9 +1282,9 @@ def test_validate_unaryop(): lhs_symbol, rhs_symbol]) with pytest.raises(TangentLinearError) as info: trans.validate(assignment) - assert ("Each term on the RHS of the assignment 'a = SQRT(x=b)\n' must " + assert ("Each term on the RHS of the assignment 'a = SQRT(b)\n' must " "be linear with respect to the active variable, but found " - "'SQRT(x=b)'." in str(info.value)) + "'SQRT(b)'." in str(info.value)) def test_validate_mismatched_array_ranges(): diff --git a/src/psyclone/tests/psyad/transformations/test_preprocess.py b/src/psyclone/tests/psyad/transformations/test_preprocess.py index 1f0f91e266..bce1b512a4 100644 --- a/src/psyclone/tests/psyad/transformations/test_preprocess.py +++ b/src/psyclone/tests/psyad/transformations/test_preprocess.py @@ -101,7 +101,7 @@ def test_preprocess_reference2arrayrange(tmpdir, fortran_reader, " a(idx_1,idx) = b(idx_1,idx) * c(idx_1,idx)\n" " enddo\n" " enddo\n" - " do i = LBOUND(array=d, dim=1), UBOUND(array=d, dim=1), 1\n" + " do i = LBOUND(d, dim=1), UBOUND(d, dim=1), 1\n" " d(i) = 0.0\n" " enddo\n" " e(:,:) = f(:,:)\n\n" @@ -203,8 +203,8 @@ def test_preprocess_arrayassign2loop(tmpdir, fortran_reader, fortran_writer): expected = ( " integer :: idx\n" " integer :: idx_1\n\n" - " do idx = LBOUND(array=a, dim=3), UBOUND(array=a, dim=3), 1\n" - " do idx_1 = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" + " do idx = LBOUND(a, dim=3), UBOUND(a, dim=3), 1\n" + " do idx_1 = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" " a(idx_1,1,idx) = b(idx_1,1,idx) * c(idx_1,map(i) + 1,idx)\n" " enddo\n" " enddo\n" @@ -341,7 +341,7 @@ def test_preprocess_associativity4(fortran_reader, fortran_writer): " integer :: b\n" " integer, dimension(10) :: c\n" " integer, dimension(10) :: d\n\n" - " a = b * SUM(array=c(:)) + b * SUM(array=d(:))\n\n" + " a = b * SUM(c(:)) + b * SUM(d(:))\n\n" "end program test\n") psyir = fortran_reader.psyir_from_source(code) preprocess_trans(psyir, ["a", "c", "d"]) @@ -417,7 +417,7 @@ def test_associativity7(fortran_reader, fortran_writer): " integer, dimension(10) :: c\n" " integer, dimension(10) :: d\n" " type(my_type) :: mt\n\n" - " a = b * SUM(array=c(:)) + b * SUM(array=mt%x(:))\n\n" + " a = b * SUM(c(:)) + b * SUM(mt%x(:))\n\n" "end program test\n") psyir = fortran_reader.psyir_from_source(code) preprocess_trans(psyir, ["a", "c", "mt"]) diff --git a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py index cb73c742e5..f7564301fe 100644 --- a/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_gen_decls_test.py @@ -84,7 +84,7 @@ def test_gen_param_decls_dependencies(fortran_writer): assert (result == "integer, parameter :: rlg = 8\n" "integer, parameter :: wp = rlg\n" "integer, parameter :: var = rlg + wp\n" - "integer, parameter :: circle = HUGE(x=circle)\n") + "integer, parameter :: circle = HUGE(circle)\n") # Check that an (invalid, obviously) circular dependency is handled. # Replace "rlg" with a new one that depends on "wp". diff --git a/src/psyclone/tests/psyir/backend/fortran_test.py b/src/psyclone/tests/psyir/backend/fortran_test.py index 805b9c88e5..035e4845ab 100644 --- a/src/psyclone/tests/psyir/backend/fortran_test.py +++ b/src/psyclone/tests/psyir/backend/fortran_test.py @@ -730,7 +730,7 @@ def test_fw_gen_vardecl(fortran_writer): symbol = DataSymbol("dummy3i", INTEGER_TYPE, is_constant=True, initial_value=initval) result = fortran_writer.gen_vardecl(symbol) - assert result == "integer, parameter :: dummy3i = SIN(x=10)\n" + assert result == "integer, parameter :: dummy3i = SIN(10)\n" # Symbol has initial value but is not constant (static). This is a property # of the Fortran language and therefore is only checked for when we attempt @@ -1226,9 +1226,9 @@ def test_fw_mixed_operator_precedence(fortran_reader, fortran_writer, tmpdir): " a = -a + (-b + (-c))\n" " a = -a + (-b - (-c))\n" " b = c * (-2.0)\n" - " a = ABS(a=-b - (-c))\n" + " a = ABS(-b - (-c))\n" " e = .NOT.f .OR. (.NOT.g)\n" - " a = LOG(x=b * c)\n" + " a = LOG(b * c)\n" " a = b ** (-c)\n" " a = b ** (-b + c)\n" " a = (-b) ** c\n" @@ -1330,9 +1330,9 @@ def test_fw_range(fortran_writer): range2 = Range.create(dim2_bound_start, plus, step=three) # Check the ranges in isolation result = fortran_writer(range1) - assert result == "1:UBOUND(array=a, dim=1)" + assert result == "1:UBOUND(a, dim=1)" result = fortran_writer(range2) - assert result == "LBOUND(array=a, dim=2):b + c:3" + assert result == "LBOUND(a, dim=2):b + c:3" # Check the ranges in context array = ArrayReference.create( symbol, [range1, range2]) @@ -1367,8 +1367,8 @@ def test_fw_range(fortran_writer): Range.create(dim3_bound_stop.copy(), dim3_bound_start.copy(), step=three.copy())]) result = fortran_writer.arrayreference_node(array) - assert result == ("a(LBOUND(array=b, dim=1):UBOUND(array=b, dim=1),:2:3," - "UBOUND(array=a, dim=3):LBOUND(array=a, dim=3):3)") + assert result == ("a(LBOUND(b, dim=1):UBOUND(b, dim=1),:2:3," + "UBOUND(a, dim=3):LBOUND(a, dim=3):3)") def test_fw_range_structureref(fortran_writer): @@ -1404,8 +1404,8 @@ def test_fw_range_structureref(fortran_writer): ArrayOfStructuresReference.create(array_symbol, [range2], [("data", [range2.copy()])])) assert (result == - "my_grids(:)%data(LBOUND(array=my_grids, dim=1):" - "UBOUND(array=my_grids, dim=1))") + "my_grids(:)%data(LBOUND(my_grids, dim=1):" + "UBOUND(my_grids, dim=1))") symbol = DataSymbol("field", UnresolvedType()) int_one = Literal("1", INTEGER_TYPE) @@ -1423,10 +1423,10 @@ def test_fw_range_structureref(fortran_writer): ("second", [my_range.copy()])]) result = fortran_writer(ref) assert (result == - "field(LBOUND(array=field%first, dim=1):" - "UBOUND(array=field%first, dim=1))%first%second(" - "LBOUND(array=field%first, dim=1):" - "UBOUND(array=field%first, dim=1))") + "field(LBOUND(field%first, dim=1):" + "UBOUND(field%first, dim=1))%first%second(" + "LBOUND(field%first, dim=1):" + "UBOUND(field%first, dim=1))") data_ref = Reference(array_symbol) start = IntrinsicCall.create( @@ -1558,7 +1558,7 @@ def test_fw_unaryoperator2(fortran_reader, fortran_writer, tmpdir): # Generate Fortran from the PSyIR schedule result = fortran_writer(schedule) - assert "a = SIN(x=1.0)" in result + assert "a = SIN(1.0)" in result assert Compile(tmpdir).string_compiles(result) @@ -1697,9 +1697,9 @@ def test_fw_query_intrinsics(fortran_reader, fortran_writer, tmpdir): # Generate Fortran from the PSyIR result = fortran_writer(psyir).lower() - assert "mysize = size(array=a, dim=2)" in result - assert "lb = lbound(array=a, dim=2)" in result - assert "ub = ubound(array=a, dim=2)" in result + assert "mysize = size(a, dim=2)" in result + assert "lb = lbound(a, dim=2)" in result + assert "ub = ubound(a, dim=2)" in result assert Compile(tmpdir).string_compiles(result) @@ -1851,16 +1851,17 @@ def test_fw_intrinsic_output_control(fortran_writer): Reference(DataSymbol("arg2", REAL_TYPE))] call = IntrinsicCall.create(IntrinsicCall.Intrinsic.ISHFT, args) - result = fortran_writer(call) - # Default behaviour is to include names of all arguments. - assert "ISHFT(i=arg1, shift=arg2)" in result - # Turn off the output of names of required arguments - Config.get().backend_intrinsic_named_kwargs = False + # Default behaviour is to remove the names of required arguments result = fortran_writer(call) assert "ISHFT(arg1, arg2)" in result + # Enable output of all arguments' names. + Config.get().backend_intrinsic_named_kwargs = True + result = fortran_writer(call) + assert "ISHFT(i=arg1, shift=arg2)" in result - # Check that optional arguments are still output. + Config.get().backend_intrinsic_named_kwargs = False + # Check that optional arguments are always output. args = [Reference(DataSymbol("arg1", REAL_TYPE)), Reference(DataSymbol("arg2", REAL_TYPE)), Reference(DataSymbol("arg3", REAL_TYPE))] @@ -1931,7 +1932,7 @@ def test_fw_intrinsic_call_node(fortran_writer): rcall = IntrinsicCall.create(IntrinsicCall.Intrinsic.RANDOM_NUMBER, [Reference(sym)]) gen = fortran_writer(rcall) - assert gen == "call RANDOM_NUMBER(harvest=var)\n" + assert gen == "call RANDOM_NUMBER(var)\n" # Test an intrinsic with no cap on arguments. rcall = IntrinsicCall.create(IntrinsicCall.Intrinsic.MAX, @@ -1947,14 +1948,14 @@ def test_fw_intrinsic_call_node(fortran_writer): intrinsic_function, [Reference(sym)]) assignment = Assignment.create(Reference(sym), intrinsic_call) gen = fortran_writer(assignment) - assert gen == f"var = {intrinsic_function.name}(array=var)\n" + assert gen == f"var = {intrinsic_function.name}(var)\n" for intrinsic_function in [IntrinsicCall.Intrinsic.TINY, IntrinsicCall.Intrinsic.HUGE]: intrinsic_call = IntrinsicCall.create( intrinsic_function, [Reference(sym)]) assignment = Assignment.create(Reference(sym), intrinsic_call) gen = fortran_writer(assignment) - assert gen == f"var = {intrinsic_function.name}(x=var)\n" + assert gen == f"var = {intrinsic_function.name}(var)\n" def test_fw_comments(fortran_writer): diff --git a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py index 478450ce61..f9b9569194 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_intrinsic_handler_test.py @@ -103,7 +103,7 @@ def test_intrinsic_handler_intrinsiccall_mms( intrinsic_call = psyir.children[0].children[0].children[1] assert isinstance(intrinsic_call, IntrinsicCall) result = fortran_writer(intrinsic_call) - assert result == f"{intrinsic_name}(array=a, dim=d, mask=m)" + assert result == f"{intrinsic_name}(a, d, mask=m)" routine_symbol = intrinsic_call.routine.symbol assert isinstance(routine_symbol, RoutineSymbol) @@ -137,7 +137,7 @@ def test_intrinsic_handler_intrinsiccall_onearg( intrinsic_call = psyir.children[0].children[0].children[1] assert isinstance(intrinsic_call, IntrinsicCall) result = fortran_writer(intrinsic_call) - assert result == f"{intrinsic_name}(x=a)" + assert result == f"{intrinsic_name}(a)" routine_symbol = intrinsic_call.routine.symbol assert isinstance(routine_symbol, RoutineSymbol) assert intrinsic_call.routine.name == intrinsic_name diff --git a/src/psyclone/tests/psyir/frontend/fparser2_test.py b/src/psyclone/tests/psyir/frontend/fparser2_test.py index ae9b56a254..58b1dd0de6 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_test.py @@ -1201,7 +1201,7 @@ def test_process_array_declarations_bound_expressions(): assert l5dtype.shape[0].lower.intrinsic is IntrinsicCall.Intrinsic.NINT assert isinstance(l5dtype.shape[0].upper, IntrinsicCall) assert l5dtype.shape[0].upper.intrinsic is IntrinsicCall.Intrinsic.NINT - assert l5dtype.shape[0].upper.debug_string() == "NINT(a=MAXVAL(array=l4))" + assert l5dtype.shape[0].upper.debug_string() == "NINT(MAXVAL(l4))" @pytest.mark.usefixtures("f2008_parser") diff --git a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py index 221fc9c88d..0d77e7490b 100644 --- a/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py +++ b/src/psyclone/tests/psyir/frontend/fparser2_where_handler_test.py @@ -280,15 +280,15 @@ def test_basic_where(): assert isinstance(loop.ast, Fortran2003.Where_Construct) assert isinstance(loops[0].start_expr, Literal) - assert loops[0].stop_expr.debug_string() == "SIZE(array=dry, dim=3)" + assert loops[0].stop_expr.debug_string() == "SIZE(dry, dim=3)" ifblock = loops[2].loop_body[0] assert isinstance(ifblock, IfBlock) assert "was_where" in ifblock.annotations assert (ifblock.condition.debug_string() == - "dry(LBOUND(array=dry, dim=1) + widx1 - 1," - "LBOUND(array=dry, dim=2) + widx2 - 1," - "LBOUND(array=dry, dim=3) + widx3 - 1)") + "dry(LBOUND(dry, dim=1) + widx1 - 1," + "LBOUND(dry, dim=2) + widx2 - 1," + "LBOUND(dry, dim=3) + widx3 - 1)") @pytest.mark.usefixtures("parser") @@ -315,9 +315,9 @@ def test_where_array_subsections(): assert isinstance(assign, Assignment) assert isinstance(assign.lhs.children[0], BinaryOperation) assert (assign.lhs.children[0].debug_string() == - "LBOUND(array=z1_st, dim=1) + widx1 - 1") + "LBOUND(z1_st, dim=1) + widx1 - 1") assert (assign.lhs.children[2].debug_string() == - "LBOUND(array=z1_st, dim=3) + widx2 - 1") + "LBOUND(z1_st, dim=3) + widx2 - 1") def test_where_mask_starting_value(fortran_reader, fortran_writer): @@ -423,11 +423,11 @@ def test_where_mask_is_slice_lower_limit(fortran_reader, fortran_writer): psyir = fortran_reader.psyir_from_source(code) out = fortran_writer(psyir) # Check that created loops have the correct number of iterations - assert "do widx2 = 1, UBOUND(array=picefr, dim=2) - jstart + 1, 1" in out - assert "do widx1 = 1, 4 - LBOUND(array=picefr, dim=1) + 1, 1" in out + assert "do widx2 = 1, UBOUND(picefr, dim=2) - jstart + 1, 1" in out + assert "do widx1 = 1, 4 - LBOUND(picefr, dim=1) + 1, 1" in out # Check that the indexing into the mask expression uses the lower bounds # specified in the original slice. - assert ("if (picefr(LBOUND(array=picefr, dim=1) + widx1 - 1," + assert ("if (picefr(LBOUND(picefr, dim=1) + widx1 - 1," "jstart + widx2 - 1) > 1.e-10)" in out) assert ("zevap_ice(widx1,widx2,1) = 0.0" in out) assert "if (picefr(4 + widx1 - 1,jstart + widx2 - 1) > 4.e-10)" in out @@ -496,8 +496,8 @@ def test_where_containing_sum_no_dim(fortran_reader, fortran_writer): routine = psyir.walk(Routine)[0] assert isinstance(routine[0], Loop) output = fortran_writer(psyir) - assert ("SUM(array=a_i_last_couple) / picefr(LBOUND(array=picefr, dim=1) " - "+ widx1 - 1,LBOUND(array=picefr, dim=2) + widx2 - 1)" in output) + assert ("SUM(a_i_last_couple) / picefr(LBOUND(picefr, dim=1) " + "+ widx1 - 1,LBOUND(picefr, dim=2) + widx2 - 1)" in output) def test_where_mask_containing_sum_with_dim(fortran_reader): @@ -555,8 +555,8 @@ def test_where_with_scalar_assignment(fortran_reader, fortran_writer): integer :: widx2 integer :: widx1 - do widx2 = 1, SIZE(array=dry, dim=3), 1 - do widx1 = 1, SIZE(array=dry, dim=2), 1 + do widx2 = 1, SIZE(dry, dim=3), 1 + do widx1 = 1, SIZE(dry, dim=2), 1 if (dry(1,widx1,widx2)) then var1 = depth z1_st(widx1,2,widx2) = var1 / ptsu(widx1,widx2,3) @@ -630,9 +630,9 @@ def test_elsewhere(): assert isinstance(ifblock.condition, BinaryOperation) assert ifblock.condition.operator == BinaryOperation.Operator.GT assert (ifblock.condition.debug_string() == - "ptsu(LBOUND(array=ptsu, dim=1) + widx1 - 1," - "LBOUND(array=ptsu, dim=2) + widx2 - 1," - "LBOUND(array=ptsu, dim=3) + widx3 - 1) > 10._wp") + "ptsu(LBOUND(ptsu, dim=1) + widx1 - 1," + "LBOUND(ptsu, dim=2) + widx2 - 1," + "LBOUND(ptsu, dim=3) + widx3 - 1) > 10._wp") # Check that this IF block has an else body which contains another IF assert ifblock.else_body is not None ifblock2 = ifblock.else_body[0] @@ -641,9 +641,9 @@ def test_elsewhere(): assert isinstance(ifblock2.condition, BinaryOperation) assert ifblock2.condition.operator == BinaryOperation.Operator.LT assert (ifblock2.condition.debug_string() == - "ptsu(LBOUND(array=ptsu, dim=1) + widx1 - 1," - "LBOUND(array=ptsu, dim=2) + widx2 - 1," - "LBOUND(array=ptsu, dim=3) + widx3 - 1) < 0.0_wp") + "ptsu(LBOUND(ptsu, dim=1) + widx1 - 1," + "LBOUND(ptsu, dim=2) + widx2 - 1," + "LBOUND(ptsu, dim=3) + widx3 - 1) < 0.0_wp") # Check that this IF block too has an else body assert isinstance(ifblock2.else_body[0], Assignment) # Check that we have three assignments of the correct form and with the @@ -653,9 +653,9 @@ def test_elsewhere(): for assign in assigns: assert isinstance(assign.lhs, ArrayReference) assert (assign.lhs.debug_string() == - "z1_st(LBOUND(array=z1_st, dim=1) + widx1 - 1," - "LBOUND(array=z1_st, dim=2) + widx2 - 1," - "LBOUND(array=z1_st, dim=3) + widx3 - 1)") + "z1_st(LBOUND(z1_st, dim=1) + widx1 - 1," + "LBOUND(z1_st, dim=2) + widx2 - 1," + "LBOUND(z1_st, dim=3) + widx3 - 1)") assert isinstance(assign.parent.parent, IfBlock) assert isinstance(assigns[0].rhs, BinaryOperation) @@ -809,7 +809,7 @@ def test_where_derived_type(fortran_reader, code, size_arg): assert len(loops) == 2 assert isinstance(loops[1].stop_expr, IntrinsicCall) assert (loops[1].stop_expr.debug_string() == - f"SIZE(array={size_arg}, dim=1)") + f"SIZE({size_arg}, dim=1)") assert isinstance(loops[1].loop_body[0], IfBlock) # All Range nodes should have been replaced assert not loops[0].walk(Range) @@ -959,7 +959,7 @@ def test_non_array_reduction_intrinsic(fortran_reader, fortran_writer): do widx1 = 1, 100, 1 if (a(widx1) < b(widx1)) then - c(widx1) = SIN(x=a(widx1)) + c(widx1) = SIN(a(widx1)) end if enddo @@ -1038,7 +1038,7 @@ def test_elemental_intrinsic_to_loop(fortran_reader, fortran_writer): integer :: widx1 do widx1 = 1, 100, 1 - if (ABS(a=a(widx1)) < 2) then + if (ABS(a(widx1)) < 2) then b(widx1) = a(widx1) end if enddo @@ -1053,7 +1053,7 @@ def test_elemental_intrinsic_to_loop(fortran_reader, fortran_writer): implicit none real, dimension(100) :: a, b - where(dot_product(a,a(:)) + abs(a=a) < 2) + where(dot_product(a,a(:)) + abs(a) < 2) b = a end where end program @@ -1062,7 +1062,7 @@ def test_elemental_intrinsic_to_loop(fortran_reader, fortran_writer): out = fortran_writer(psyir) assert isinstance(psyir.children[0].children[0], Loop) correct = '''do widx1 = 1, 100, 1 - if (DOT_PRODUCT(vector_a=a, vector_b=a(:)) + ABS(a=a(widx1)) < 2) then + if (DOT_PRODUCT(a, a(:)) + ABS(a(widx1)) < 2) then b(widx1) = a(widx1) end if enddo''' @@ -1126,7 +1126,7 @@ def test_elemental_function_to_loop(fortran_reader, fortran_writer): psyir = fortran_reader.psyir_from_source(code) assert isinstance(psyir.children[0].children[1].children[0], Loop) correct = '''do widx1 = 1, 100, 1 - if (x(a(:)) + ABS(a=a(widx1)) < 2) then + if (x(a(:)) + ABS(a(widx1)) < 2) then b(widx1) = a(widx1) end if enddo''' diff --git a/src/psyclone/tests/psyir/nodes/array_mixin_test.py b/src/psyclone/tests/psyir/nodes/array_mixin_test.py index 349609c68c..f5d428fda8 100644 --- a/src/psyclone/tests/psyir/nodes/array_mixin_test.py +++ b/src/psyclone/tests/psyir/nodes/array_mixin_test.py @@ -526,7 +526,7 @@ def test_member_get_bound_expression(fortran_writer): lbnd = ref.member._get_bound_expression(0, "lower") assert isinstance(lbnd, IntrinsicCall) out = fortran_writer(lbnd).lower() - assert "lbound(array=grid_var%data, dim=1)" in out + assert "lbound(grid_var%data, dim=1)" in out usym = DataSymbol("uvar", UnresolvedType()) ref = ArrayOfStructuresReference.create( usym, [_ONE.copy()], @@ -535,12 +535,12 @@ def test_member_get_bound_expression(fortran_writer): lbnd = ref.member.member._get_bound_expression(0, "lower") assert isinstance(lbnd, IntrinsicCall) out = fortran_writer(lbnd).lower() - assert "lbound(array=uvar(1)%map(1,2)%data, dim=1)" in out + assert "lbound(uvar(1)%map(1,2)%data, dim=1)" in out ubnd = ref.member._get_bound_expression(0, "upper") assert isinstance(ubnd, IntrinsicCall) out = fortran_writer(ubnd).lower() - assert "ubound(array=uvar(1)%map, dim=1)" in out + assert "ubound(uvar(1)%map, dim=1)" in out # Second, test when we do have type information. a2d = ArrayType(REAL_TYPE, [2, (2, 8)]) # Structure that contains "map" which is a 2D array. @@ -659,19 +659,19 @@ def test_get_effective_shape(fortran_reader): child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert "SIZE(array=a, dim=1)" in shape[0].debug_string() + assert "SIZE(a, dim=1)" in shape[0].debug_string() # Array slice with only lower-bound specified. # a(2:) = 0.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert shape[0].debug_string() == "UBOUND(array=a, dim=1) - 2 + 1" + assert shape[0].debug_string() == "UBOUND(a, dim=1) - 2 + 1" # Array slice with only upper-bound specified. # a(:5) = 0.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert shape[0].debug_string() == "5 - LBOUND(array=a, dim=1) + 1" + assert shape[0].debug_string() == "5 - LBOUND(a, dim=1) + 1" # Array slice with only step specified. # a(::4) = 0.0 child_idx += 1 @@ -680,7 +680,7 @@ def test_get_effective_shape(fortran_reader): # Since step is not unity, this is not a 'full-range' access so we still # end up with UBOUND and LBOUND, even though SIZE would be nicer. assert (shape[0].debug_string() == - "(UBOUND(array=a, dim=1) - LBOUND(array=a, dim=1)) / 4 + 1") + "(UBOUND(a, dim=1) - LBOUND(a, dim=1)) / 4 + 1") # Array slice defined using LBOUND and UBOUND intrinsics but for a # different array altogether. # a(lbound(b,1):ubound(b,2)) = 0.0 @@ -688,7 +688,7 @@ def test_get_effective_shape(fortran_reader): shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 assert (shape[0].debug_string() == - "UBOUND(array=b, dim=2) - LBOUND(array=b, dim=1) + 1") + "UBOUND(b, dim=2) - LBOUND(b, dim=1) + 1") # Indirect array slice. # b(indices(2:3,1), 2:5) = 2.0 child_idx += 1 @@ -725,14 +725,14 @@ def test_get_effective_shape(fortran_reader): shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 assert (shape[0].debug_string().lower() == - "ubound(array=b, dim=2) - (1 + indices(1,1)) + 1") + "ubound(b, dim=2) - (1 + indices(1,1)) + 1") # Array access with indices given by another array that is not explicitly # indexed. # b(idx, a) = -1.0 child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() assert len(shape) == 1 - assert "SIZE(array=a)" in shape[0].debug_string() + assert "SIZE(a)" in shape[0].debug_string() # Array-index expressions are symbols of unknown type so we don't know # whether we have an array slice or just a scalar. # b(scalarval, arrayval) = 1 @@ -764,8 +764,8 @@ def test_struct_get_effective_shape(fortran_reader): child_idx = 0 shape = routine.children[child_idx].lhs.member._get_effective_shape() assert len(shape) == 2 - assert "SIZE(array=grid%data, dim=1)" in shape[0].debug_string() - assert "SIZE(array=grid%data, dim=2)" in shape[1].debug_string() + assert "SIZE(grid%data, dim=1)" in shape[0].debug_string() + assert "SIZE(grid%data, dim=2)" in shape[1].debug_string() # Slice of ArrayOfStructuresMixin child_idx += 1 shape = routine.children[child_idx].lhs._get_effective_shape() diff --git a/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py b/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py index 5b3b3eb870..6f68ff33f0 100644 --- a/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py +++ b/src/psyclone/tests/psyir/nodes/dynamic_omp_task_directive_test.py @@ -2817,12 +2817,12 @@ def test_omp_task_directive_nemolite_boundary( if (.NOT.boundary(i,j) + boundary(i,j + 1) <= (-1)) then if (boundary(i,j) < 0) then jiv = j + 1 - va(i,j) = va(i,jiv) + SQRT(x=g / hv(i,j)) * (sshn_v(i,j) - \ + va(i,j) = va(i,jiv) + SQRT(g / hv(i,j)) * (sshn_v(i,j) - \ sshn_v(i,jiv)) else if (boundary(i,j + 1) < 0) then jiv = j - 1 - va(i,j) = va(i,jiv) + SQRT(x=g / hv(i,j)) * (sshn_v(i,j) - \ + va(i,j) = va(i,jiv) + SQRT(g / hv(i,j)) * (sshn_v(i,j) - \ sshn_v(i,jiv)) end if end if @@ -3230,7 +3230,7 @@ def test_omp_task_directive_disallowed_intrinsic(fortran_reader): with pytest.raises(GenerationError) as excinfo: tree.lower_to_language_level() assert ("Attempted to lower to OMPTaskDirective node, but the " - "node contains a 'SUM(array=b)' intrinsic call, which is not " + "node contains a 'SUM(b)' intrinsic call, which is not " "supported." in str(excinfo.value)) @@ -3271,8 +3271,8 @@ def test_omp_task_directive_intrinsic_loop_bound(fortran_reader, ptrans.apply(parent.children) correct = '''\ !$omp task private(i,j) shared(a,b) depend(in: b(:,:)) depend(out: a(:,:)) - do i = LBOUND(array=a, dim=2), UBOUND(array=a, dim=2), 1 - do j = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1 + do i = LBOUND(a, dim=2), UBOUND(a, dim=2), 1 + do j = LBOUND(a, dim=1), UBOUND(a, dim=1), 1 a(i,j) = b(i,j) + 1 enddo enddo @@ -3319,7 +3319,7 @@ def test_omp_task_directive_intrinsic_loop_step(fortran_reader): tdir.lower_to_language_level() assert ("IntrinsicCall not supported in the step variable of a Loop" " in an OMPTaskDirective node. The step expression is " - "'LBOUND(array=a, dim=2)'." in str(excinfo.value)) + "'LBOUND(a, dim=2)'." in str(excinfo.value)) def test_evaluate_write_reference_failcase(): diff --git a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py index e4cc549721..c8d9fceabf 100644 --- a/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py +++ b/src/psyclone/tests/psyir/nodes/intrinsic_call_test.py @@ -543,10 +543,10 @@ def test_index_intrinsic(fortran_reader, fortran_writer): psyir = fortran_reader.psyir_from_source(code) assert len(psyir.walk(IntrinsicCall)) == 3 result = fortran_writer(psyir).lower() - assert ("ind1 = index(string=clname, substring='_', back=.true.) + 1" in + assert ("ind1 = index(clname, '_', back=.true.) + 1" in result) - assert "ind2 = index(string=clname, substring='.') - 1" in result - assert "ind2 = index(string=clname, substring='.', kind=4) - 1" in result + assert "ind2 = index(clname, '.') - 1" in result + assert "ind2 = index(clname, '.', kind=4) - 1" in result def test_verify_intrinsic(fortran_reader, fortran_writer): @@ -575,14 +575,14 @@ def test_verify_intrinsic(fortran_reader, fortran_writer): # Should have 4 VERIFY and 2 KIND assert len(psyir.walk(IntrinsicCall)) == 6 result = fortran_writer(psyir).lower() - assert ("if (verify(string=clname(ind1:ind2), set='0123456789') == 0) " + assert ("if (verify(clname(ind1:ind2), '0123456789') == 0) " "then" in result) - assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " + assert ("if (verify(clname(ind1:ind2), '0123456789', " "back=.true.) == 0) then" in result) - assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " - "kind=kind(x=1)) == 0) then" in result) - assert ("if (verify(string=clname(ind1:ind2), set='0123456789', " - "kind=kind(x=1), back=.true.) == 0) then" in result) + assert ("if (verify(clname(ind1:ind2), '0123456789', " + "kind=kind(1)) == 0) then" in result) + assert ("if (verify(clname(ind1:ind2), '0123456789', " + "kind=kind(1), back=.true.) == 0) then" in result) def test_intrinsic_compute_argument_names_value_errors(): diff --git a/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py b/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py index ae65c64fde..f04e60c63b 100644 --- a/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py +++ b/src/psyclone/tests/psyir/nodes/type_convert_intrinsic_test.py @@ -64,13 +64,13 @@ def test_type_convert_intrinsic_create(intrinsic, intr_str, fortran_writer): assert intr_call.intrinsic is intrinsic check_links(intr_call, [intr_call.routine, lhs, rhs]) result = fortran_writer(intr_call) - assert intr_str + "(a=tmp1, kind=wp)" in result.lower() + assert intr_str + "(tmp1, kind=wp)" in result.lower() # Kind specified with an integer literal rhs = Literal("4", INTEGER_SINGLE_TYPE) intr_call = IntrinsicCall.create(intrinsic, [lhs.detach(), ("kind", rhs)]) check_links(intr_call, [intr_call.routine, lhs, rhs]) result = fortran_writer(intr_call) - assert intr_str + "(a=tmp1, kind=4)" in result.lower() + assert intr_str + "(tmp1, kind=4)" in result.lower() # Kind specified as an arithmetic expression rhs = BinaryOperation.create(BinaryOperation.Operator.ADD, Reference(wp_sym), @@ -78,7 +78,7 @@ def test_type_convert_intrinsic_create(intrinsic, intr_str, fortran_writer): intr_call = IntrinsicCall.create(intrinsic, [lhs.detach(), ("kind", rhs)]) check_links(intr_call, [intr_call.routine, lhs, rhs]) result = fortran_writer(intr_call) - assert intr_str + "(a=tmp1, kind=wp + 2)" in result.lower() + assert intr_str + "(tmp1, kind=wp + 2)" in result.lower() @pytest.mark.xfail(reason="No PSyIR symbol type checking is performed on the " diff --git a/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py b/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py index 88371221ff..9c703fa7ed 100644 --- a/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/acc_kernels_trans_test.py @@ -278,12 +278,12 @@ def test_kernels_around_where_construct(fortran_reader, fortran_writer): assert isinstance(schedule[0], ACCKernelsDirective) assert isinstance(schedule[0].dir_body[0], Loop) assert (" !$acc kernels\n" - " do widx2 = 1, SIZE(array=a(:,:), dim=2), 1\n" - " do widx1 = 1, SIZE(array=a(:,:), dim=1), 1\n" - " if (a(LBOUND(array=a, dim=1) + widx1 - 1," - "LBOUND(array=a, dim=2) + widx2 - 1) < flag) then\n" - " b(LBOUND(array=b, dim=1) + widx1 - 1," - "LBOUND(array=b, dim=2) + widx2 - 1) = 0.0\n" + " do widx2 = 1, SIZE(a(:,:), dim=2), 1\n" + " do widx1 = 1, SIZE(a(:,:), dim=1), 1\n" + " if (a(LBOUND(a, dim=1) + widx1 - 1," + "LBOUND(a, dim=2) + widx2 - 1) < flag) then\n" + " b(LBOUND(b, dim=1) + widx1 - 1," + "LBOUND(b, dim=2) + widx2 - 1) = 0.0\n" " end if\n" " enddo\n" " enddo\n" @@ -305,12 +305,12 @@ def test_kernels_around_where_stmt(fortran_reader, fortran_writer): acc_trans.apply([schedule[1]]) assert (" a(:,:) = 1.0\n" " !$acc kernels\n" - " do widx2 = 1, SIZE(array=a(:,:), dim=2), 1\n" - " do widx1 = 1, SIZE(array=a(:,:), dim=1), 1\n" - " if (a(LBOUND(array=a, dim=1) + widx1 - 1," - "LBOUND(array=a, dim=2) + widx2 - 1) < flag) then\n" - " b(LBOUND(array=b, dim=1) + widx1 - 1," - "LBOUND(array=b, dim=2) + widx2 - 1) = 0.0\n" + " do widx2 = 1, SIZE(a(:,:), dim=2), 1\n" + " do widx1 = 1, SIZE(a(:,:), dim=1), 1\n" + " if (a(LBOUND(a, dim=1) + widx1 - 1," + "LBOUND(a, dim=2) + widx2 - 1) < flag) then\n" + " b(LBOUND(b, dim=1) + widx1 - 1," + "LBOUND(b, dim=2) + widx2 - 1) = 0.0\n" " end if\n" " enddo\n" " enddo\n" @@ -471,12 +471,12 @@ def test_no_assumed_size_char_in_kernels(fortran_reader): with pytest.raises(TransformationError) as err: acc_trans.validate(sub.children[1], options={"allow_string": True}) assert ("Assumed-size character variables cannot be enclosed in an OpenACC" - " region but found 'assumed_size_char(:LEN(string=" + " region but found 'assumed_size_char(:LEN(" "explicit_size_char)) = " in str(err.value)) with pytest.raises(TransformationError) as err: acc_trans.validate(sub.children[2], options={"allow_string": True}) - assert ("Cannot include 'ACHAR(i=9)' in an OpenACC region because " + assert ("Cannot include 'ACHAR(9)' in an OpenACC region because " "it is not available on GPU" in str(err.value)) # Check that the character assignment is excluded by default. with pytest.raises(TransformationError) as err: diff --git a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py index 63946b586f..9bab2c6928 100644 --- a/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/arrayassignment2loops_trans_test.py @@ -65,70 +65,70 @@ def test_str(): # Scalar RHS [("integer, dimension(:) :: x, y, z, t\n" "x(:) = 0.0", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = 0.0\n"), # Array LHS and RHS ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y(:)\n", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = y(idx)\n"), # Multi-dimensional array LHS and RHS ("integer, dimension(:,:,:) :: x, y, z, t\n" "x(:,:,:) = y(:,:,:)\n", - " do idx = LBOUND(array=x, dim=3), UBOUND(array=x, dim=3), 1\n" - " do idx_1 = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" - " do idx_2 = LBOUND(array=x, dim=1), " - "UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=3), UBOUND(x, dim=3), 1\n" + " do idx_1 = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" + " do idx_2 = LBOUND(x, dim=1), " + "UBOUND(x, dim=1), 1\n" " x(idx_2,idx_1,idx) = y(idx_2,idx_1,idx)\n"), # Multiple arrays on RHS ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y(:) + z(:) * t(:)\n", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = y(idx) + z(idx) * t(idx)\n"), # Argument of elemental functions are expanded ("integer, dimension(:) :: x, y, z, t\n" "x(:) = max(y(:), z(:))\n", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = MAX(y(idx), z(idx))\n"), # Argument of inquiry functions are NOT expanded ("integer, dimension(:) :: x, y, z, t\n" "x(:) = y + size(y)\n", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" - " x(idx) = y(idx) + SIZE(array=y)\n"), + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" + " x(idx) = y(idx) + SIZE(y)\n"), # Mix different array ranks with fixed indices ("integer, dimension(:) :: x, z, t\n" "integer, dimension(:,:) :: y\n" "x(:) = y(n,:)\n", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = y(n,idx)\n"), ("integer, dimension(:,:) :: x, z, t\n" "integer, dimension(:) :: y\n" "x(n,:) = y(:)\n", - " do idx = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" + " do idx = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" " x(n,idx) = y(idx)\n"), ("integer, dimension(:,:) :: x, z, t\n" "integer, dimension(:,:,:) :: y\n" "x(:,:)=y(:,n,:)\n", - " do idx = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" - " do idx_1 = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" + " do idx_1 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx_1,idx) = y(idx_1,n,idx)\n"), # Same rank but different range locations ("integer, parameter :: jpi=2, jpj=4, jpk=6, jpt=9, ndim=10\n" "real, dimension(jpi,jpj,jpk,jpt,ndim) :: x, y, z, t\n" "x(:,jpj,:,ndim,:) = y(jpi,:,:,:,ndim) + 1.0\n", - " do idx = LBOUND(array=x, dim=5), UBOUND(array=x, dim=5), 1\n" - " do idx_1 = LBOUND(array=x, dim=3), UBOUND(array=x, dim=3), 1\n" - " do idx_2 = LBOUND(array=x, dim=1), " - "UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=5), UBOUND(x, dim=5), 1\n" + " do idx_1 = LBOUND(x, dim=3), UBOUND(x, dim=3), 1\n" + " do idx_2 = LBOUND(x, dim=1), " + "UBOUND(x, dim=1), 1\n" " x(idx_2,jpj,idx_1,ndim,idx) = y(jpi,idx_2,idx_1," "idx,ndim) + 1.0\n" " enddo\n" @@ -153,14 +153,14 @@ def test_str(): # uses L/UBOUND which si correct). ("integer, dimension(2:4) :: x, y, z, t\n" "x(:) = 0", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = 0\n"), # Explicit lower bound value (assumed-shape array) - still just # uses LBOUND. ("integer, dimension(2:) :: x, y, z, t\n" "x(:) = 0", - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = 0\n"), # Combine multiple previous features @@ -173,42 +173,42 @@ def test_str(): # SoA in LHS ("integer :: x, y, z, t\n" "mystruct%soa%array(:,:,:) = 0.0d0", - " do idx = LBOUND(array=mystruct%soa%array, dim=3), " - "UBOUND(array=mystruct%soa%array, dim=3), 1\n" - " do idx_1 = LBOUND(array=mystruct%soa%array, dim=2), " - "UBOUND(array=mystruct%soa%array, dim=2), 1\n" - " do idx_2 = LBOUND(array=mystruct%soa%array, dim=1), " - "UBOUND(array=mystruct%soa%array, dim=1), 1\n" + " do idx = LBOUND(mystruct%soa%array, dim=3), " + "UBOUND(mystruct%soa%array, dim=3), 1\n" + " do idx_1 = LBOUND(mystruct%soa%array, dim=2), " + "UBOUND(mystruct%soa%array, dim=2), 1\n" + " do idx_2 = LBOUND(mystruct%soa%array, dim=1), " + "UBOUND(mystruct%soa%array, dim=1), 1\n" " mystruct%soa%array(idx_2,idx_1,idx) = 0.0d0\n"), # Array in LHS and SoA in RHS ("integer, dimension(:,:,:) :: x, y, z, t\n" "x(:,:,:) = 3 + mystruct%soa%array(:,:,:)", - " do idx = LBOUND(array=x, dim=3), UBOUND(array=x, dim=3), 1\n" - " do idx_1 = LBOUND(array=x, dim=2), UBOUND(array=x, dim=2), 1\n" - " do idx_2 = LBOUND(array=x, dim=1), " - "UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=3), UBOUND(x, dim=3), 1\n" + " do idx_1 = LBOUND(x, dim=2), UBOUND(x, dim=2), 1\n" + " do idx_2 = LBOUND(x, dim=1), " + "UBOUND(x, dim=1), 1\n" # Ignore offset for this test, it is tested below " x(idx_2,idx_1,idx) = 3 + mystruct%soa%array(idx_2 + "), # SoAoS on LHS ("integer :: x, y, z, t\n" "mystruct%aos(:,:,:)%value = 0.0d0", - " do idx = LBOUND(array=mystruct%aos, dim=3), " - "UBOUND(array=mystruct%aos, dim=3), 1\n" - " do idx_1 = LBOUND(array=mystruct%aos, dim=2), " - "UBOUND(array=mystruct%aos, dim=2), 1\n" - " do idx_2 = LBOUND(array=mystruct%aos, dim=1), " - "UBOUND(array=mystruct%aos, dim=1), 1\n" + " do idx = LBOUND(mystruct%aos, dim=3), " + "UBOUND(mystruct%aos, dim=3), 1\n" + " do idx_1 = LBOUND(mystruct%aos, dim=2), " + "UBOUND(mystruct%aos, dim=2), 1\n" + " do idx_2 = LBOUND(mystruct%aos, dim=1), " + "UBOUND(mystruct%aos, dim=1), 1\n" " mystruct%aos(idx_2,idx_1,idx)%value = 0.0d0\n"), # SoAoS in the LHS and SoA in the RHS ("integer :: x, y, z, t\n" "mystruct%aos(:,4,:)%value = mystruct%soa%array(3,:,:)", - " do idx = LBOUND(array=mystruct%aos, dim=3), " - "UBOUND(array=mystruct%aos, dim=3), 1\n" - " do idx_1 = LBOUND(array=mystruct%aos, dim=1), " - "UBOUND(array=mystruct%aos, dim=1), 1\n" + " do idx = LBOUND(mystruct%aos, dim=3), " + "UBOUND(mystruct%aos, dim=3), 1\n" + " do idx_1 = LBOUND(mystruct%aos, dim=1), " + "UBOUND(mystruct%aos, dim=1), 1\n" # Ignore offset for this test, it is tested below " mystruct%aos(idx_1,4,idx)%value = " "mystruct%soa%array(3,idx_1 + "), @@ -217,12 +217,12 @@ def test_str(): ("integer :: x, y, z, t\n" "mystruct%aoa(4, 3)%array(:,:,:) = " "mystruct%aoa(5, 8)%array(:,:,:)", - " do idx = LBOUND(array=mystruct%aoa(4,3)%array, dim=3), " - "UBOUND(array=mystruct%aoa(4,3)%array, dim=3), 1\n" - " do idx_1 = LBOUND(array=mystruct%aoa(4,3)%array, dim=2), " - "UBOUND(array=mystruct%aoa(4,3)%array, dim=2), 1\n" - " do idx_2 = LBOUND(array=mystruct%aoa(4,3)%array, dim=1), " - "UBOUND(array=mystruct%aoa(4,3)%array, dim=1), 1\n" + " do idx = LBOUND(mystruct%aoa(4,3)%array, dim=3), " + "UBOUND(mystruct%aoa(4,3)%array, dim=3), 1\n" + " do idx_1 = LBOUND(mystruct%aoa(4,3)%array, dim=2), " + "UBOUND(mystruct%aoa(4,3)%array, dim=2), 1\n" + " do idx_2 = LBOUND(mystruct%aoa(4,3)%array, dim=1), " + "UBOUND(mystruct%aoa(4,3)%array, dim=1), 1\n" # Ignore offset for this test, it is tested below " mystruct%aoa(4,3)%array(idx_2,idx_1,idx) = " "mystruct%aoa(5,8)%array(idx_2 +")]) @@ -286,17 +286,17 @@ def test_apply_to_arrays_with_different_bounds(fortran_reader, fortran_writer): # The bounds are not known, so L/UBOUND expressions are used output_test1 = fortran_writer(psyir.walk(Assignment)[0]) - assert ("x1(idx_1,idx) = y1(idx_1 + (LBOUND(array=y1, dim=1) " - "- LBOUND(array=x1, dim=1)),idx + " - "(LBOUND(array=y1, dim=2) - LBOUND(array=x1, dim=2)))" + assert ("x1(idx_1,idx) = y1(idx_1 + (LBOUND(y1, dim=1) " + "- LBOUND(x1, dim=1)),idx + " + "(LBOUND(y1, dim=2) - LBOUND(x1, dim=2)))" in output_test1) # When we know the bounds we can see they are different, we also # need the offsets (and can also use L/UBOUND) output_test2 = fortran_writer(psyir.walk(Assignment)[1]) - assert ("x2(idx_3,idx_2) = y2(idx_3 + (LBOUND(array=y2, dim=1) " - "- LBOUND(array=x2, dim=1)),idx_2 + " - "(LBOUND(array=y2, dim=2) - LBOUND(array=x2, dim=2)))" + assert ("x2(idx_3,idx_2) = y2(idx_3 + (LBOUND(y2, dim=1) " + "- LBOUND(x2, dim=1)),idx_2 + " + "(LBOUND(y2, dim=2) - LBOUND(x2, dim=2)))" in output_test2) # If the bounds are implicit, the offset should also use the implicit @@ -310,10 +310,10 @@ def test_apply_to_arrays_with_different_bounds(fortran_reader, fortran_writer): # SoA and SoAoS bounds are also constructed using L/UBOUNDS expressions output_test4 = fortran_writer(psyir.walk(Assignment)[3]) - assert (" struct%values(idx_6 + (LBOUND(array=struct%values, dim=1) - " - "LBOUND(array=x, dim=1))) + struct%array(idx_6 + (" - "LBOUND(array=struct%array, dim=1) - " - "LBOUND(array=x, dim=1)))%value" + assert (" struct%values(idx_6 + (LBOUND(struct%values, dim=1) - " + "LBOUND(x, dim=1))) + struct%array(idx_6 + (" + "LBOUND(struct%array, dim=1) - " + "LBOUND(x, dim=1)))%value" in output_test4) @@ -390,7 +390,7 @@ def test_apply_assumed_shape(fortran_reader, fortran_writer, tmpdir): trans = ArrayAssignment2LoopsTrans() trans.apply(assign) result = fortran_writer(psyir) - assert '''do idx = istart, UBOUND(array=var, dim=1), 1 + assert '''do idx = istart, UBOUND(var, dim=1), 1 var(idx) = 2 * var2(idx + (istart2 - istart)) enddo''' in result assert Compile(tmpdir).string_compiles(result) @@ -713,10 +713,10 @@ def test_validate_rhs_plain_references(fortran_reader, fortran_writer): # The end result should look like: assert ( - " do idx = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx) = scalar\n" " enddo\n" - " do idx_1 = LBOUND(array=x, dim=1), UBOUND(array=x, dim=1), 1\n" + " do idx_1 = LBOUND(x, dim=1), UBOUND(x, dim=1), 1\n" " x(idx_1) = array(idx_1)\n" " enddo\n\n" " ! ArrayAssignment2LoopsTrans cannot expand expression because it " diff --git a/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py b/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py index 42a7a718d8..6320b13228 100644 --- a/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/hoist_local_arrays_trans_test.py @@ -138,8 +138,8 @@ def test_apply_multi_dim_imported_limits(fortran_reader, fortran_writer): # We cannot test the compilation of the generated code because of # the 'use some_mod'. assert "real, allocatable, dimension(:,:), private :: a\n" in code - assert (" if (.not.allocated(a) .or. ubound(array=a, dim=1) /= jpi " - ".or. ubound(array=a, dim=2) /= jpj) then\n" + assert (" if (.not.allocated(a) .or. ubound(a, dim=1) /= jpi " + ".or. ubound(a, dim=2) /= jpj) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" @@ -167,8 +167,8 @@ def test_apply_arg_limits(fortran_reader, fortran_writer, tmpdir): hoist_trans.apply(routine) code = fortran_writer(psyir).lower() assert "real, allocatable, dimension(:,:), private :: a\n" in code - assert (" if (.not.allocated(a) .or. ubound(array=a, dim=1) /= nx " - ".or. ubound(array=a, dim=2) /= ny) then\n" + assert (" if (.not.allocated(a) .or. ubound(a, dim=1) /= nx " + ".or. ubound(a, dim=2) /= ny) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" @@ -199,16 +199,16 @@ def test_apply_runtime_checks(fortran_reader, fortran_writer, tmpdir): hoist_trans.apply(routine) code = fortran_writer(psyir).lower() assert "real, allocatable, dimension(:,:), private :: a\n" in code - assert (" if (.not.allocated(a) .or. ubound(array=a, dim=1) /= nx " - ".or. ubound(array=a, dim=2) /= ny) then\n" + assert (" if (.not.allocated(a) .or. ubound(a, dim=1) /= nx " + ".or. ubound(a, dim=2) /= ny) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" " allocate(a(1:nx,1:ny))\n" " end if\n" in code) - assert (" if (.not.allocated(b) .or. lbound(array=b, dim=1) /= nx " - ".or. ubound(array=b, dim=1) /= ny .or. lbound(array=b, dim=2) " - "/= nx .or. ubound(array=b, dim=2) /= ny) then\n" + assert (" if (.not.allocated(b) .or. lbound(b, dim=1) /= nx " + ".or. ubound(b, dim=1) /= ny .or. lbound(b, dim=2) " + "/= nx .or. ubound(b, dim=2) /= ny) then\n" " if (allocated(b)) then\n" " deallocate(b)\n" " end if\n" @@ -244,15 +244,15 @@ def test_apply_multi_arrays(fortran_reader, fortran_writer): assert "real, allocatable, dimension(:,:), private :: a" in code assert "integer, allocatable, dimension(:,:), private :: mask" in code assert ( - " if (.not.allocated(mask) .or. ubound(array=mask, dim=1) /= jpi " - ".or. ubound(array=mask, dim=2) /= jpj) then\n" + " if (.not.allocated(mask) .or. ubound(mask, dim=1) /= jpi " + ".or. ubound(mask, dim=2) /= jpj) then\n" " if (allocated(mask)) then\n" " deallocate(mask)\n" " end if\n" " allocate(mask(1:jpi,1:jpj))\n" " end if\n" - " if (.not.allocated(a) .or. ubound(array=a, dim=1) /= nx .or. " - "ubound(array=a, dim=2) /= ny) then\n" + " if (.not.allocated(a) .or. ubound(a, dim=1) /= nx .or. " + "ubound(a, dim=2) /= ny) then\n" " if (allocated(a)) then\n" " deallocate(a)\n" " end if\n" @@ -283,8 +283,8 @@ def test_apply_name_clash(fortran_reader, fortran_writer, tmpdir): code = fortran_writer(psyir).lower() assert (" real, allocatable, dimension(:,:), private :: a\n" " real, allocatable, dimension(:,:), private :: a_2\n" in code) - assert (" if (.not.allocated(a_2) .or. ubound(array=a_2, dim=1) /= nx " - ".or. ubound(array=a_2, dim=2) /= ny) then\n" + assert (" if (.not.allocated(a_2) .or. ubound(a_2, dim=1) /= nx " + ".or. ubound(a_2, dim=2) /= ny) then\n" " if (allocated(a_2)) then\n" " deallocate(a_2)\n" " end if\n" @@ -751,14 +751,14 @@ def test_apply_with_allocatables(fortran_reader, fortran_writer, tmpdir): if (.NOT.ALLOCATED(a)) then ALLOCATE(a(10)) end if - if (.NOT.ALLOCATED(b) .OR. UBOUND(array=b, dim=1) /= var) then + if (.NOT.ALLOCATED(b) .OR. UBOUND(b, dim=1) /= var) then if (ALLOCATED(b)) then DEALLOCATE(b) end if ALLOCATE(b(10:var)) end if - if (.NOT.ALLOCATED(c) .OR. LBOUND(array=c, dim=1) /= var - 5 .OR. \ -UBOUND(array=c, dim=1) /= var + 5) then + if (.NOT.ALLOCATED(c) .OR. LBOUND(c, dim=1) /= var - 5 .OR. \ +UBOUND(c, dim=1) /= var + 5) then if (ALLOCATED(c)) then DEALLOCATE(c) end if @@ -767,7 +767,7 @@ def test_apply_with_allocatables(fortran_reader, fortran_writer, tmpdir): ! PSyclone warning: HoistLocalArraysTrans found an ALLOCATE with \ alloc-options, this is not supported - ALLOCATE(d(1:10), MOLD=arg) + ALLOCATE(d(1:10), mold=arg) ! PSyclone warning: HoistLocalArraysTrans found more than one ALLOCATE \ for this variable, but currently it just supports cases with single allocations diff --git a/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py b/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py index 4d11a4c4f6..732abc28aa 100644 --- a/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/hoist_loop_bound_expr_trans_test.py @@ -78,7 +78,7 @@ def test_apply(fortran_reader, fortran_writer): # Start expression is not hoisted because it is a literal expected = """ loop_step = mytype%step - loop_stop = UBOUND(array=a, dim=1) + loop_stop = UBOUND(a, dim=1) do i = 1, loop_stop, loop_step a(i) = 1 enddo\n""" @@ -112,10 +112,10 @@ def test_apply_nested(fortran_reader, fortran_writer): trans.apply(loop) # Start expression is not hoisted because it is a simple scalar reference expected = """ - loop_stop = UBOUND(array=a, dim=2) + loop_stop = UBOUND(a, dim=2) do i = start, loop_stop, 1 - loop_stop_1 = UBOUND(array=a, dim=1) - loop_start = LBOUND(array=a, dim=1) + loop_stop_1 = UBOUND(a, dim=1) + loop_start = LBOUND(a, dim=1) do j = loop_start, loop_stop_1, 1 a(j,i) = 1 enddo diff --git a/src/psyclone/tests/psyir/transformations/hoist_trans_test.py b/src/psyclone/tests/psyir/transformations/hoist_trans_test.py index 6a1a6b353f..09ca8e1c7f 100644 --- a/src/psyclone/tests/psyir/transformations/hoist_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/hoist_trans_test.py @@ -513,7 +513,7 @@ def test_validate_array_of_struct(fortran_reader): hoist_trans = HoistTrans() with pytest.raises(TransformationError) as err: hoist_trans.validate(loop.loop_body[0]) - assert ("The statement 'ipi = SIZE(array=ptab(jf)%pt4d, dim=1)' can't be " + assert ("The statement 'ipi = SIZE(ptab(jf)%pt4d, dim=1)' can't be " "hoisted as it reads variable 'jf'" in str(err.value)) diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py index 4adf651529..e10b169e31 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/abs2code_trans_test.py @@ -105,7 +105,7 @@ def test_correct(func, output, tmpdir): f"subroutine abs_example(arg)\n" f" real, intent(inout) :: arg\n" f" real :: psyir_tmp\n\n" - f" psyir_tmp = ABS(a={output})\n\n" + f" psyir_tmp = ABS({output})\n\n" f"end subroutine abs_example\n") in result trans = Abs2CodeTrans() trans.apply(intr_call) @@ -152,7 +152,7 @@ def test_correct_expr(tmpdir): "subroutine abs_example(arg)\n" " real, intent(inout) :: arg\n" " real :: psyir_tmp\n\n" - " psyir_tmp = 1.0 + ABS(a=arg * 3.14) + 2.0\n\n" + " psyir_tmp = 1.0 + ABS(arg * 3.14) + 2.0\n\n" "end subroutine abs_example\n") in result trans = Abs2CodeTrans() trans.apply(intr_call) @@ -199,7 +199,7 @@ def test_correct_2abs(tmpdir): "subroutine abs_example(arg)\n" " real, intent(inout) :: arg\n" " real :: psyir_tmp\n\n" - " psyir_tmp = ABS(a=arg * 3.14) + ABS(a=1.0)\n\n" + " psyir_tmp = ABS(arg * 3.14) + ABS(1.0)\n\n" "end subroutine abs_example\n") in result trans = Abs2CodeTrans() trans.apply(intr_call) diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py index 8e666816f5..f4efd3a171 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/array_reduction_base_trans_test.py @@ -251,7 +251,7 @@ def test_validate_increment_with_unsupported_type(fortran_reader): node = psyir.walk(IntrinsicCall)[0] with pytest.raises(TransformationError) as info: trans.apply(node) - assert ("To loopify 'x(1) = x(1) + MAXVAL(array=a)' we need a temporary " + assert ("To loopify 'x(1) = x(1) + MAXVAL(a)' we need a temporary " "variable, but the type of 'x(1)' can not be resolved or is " "unsupported." in str(info.value)) @@ -262,10 +262,10 @@ def test_validate_increment_with_unsupported_type(fortran_reader): [("10", "20", "1", "10", "1", "20"), ("n", "m", "1", "n", "1", "m"), ("0:n", "2:m", "0", "n", "2", "m"), - (":", ":", "LBOUND(array=array, dim=1)", - "UBOUND(array=array, dim=1)", - "LBOUND(array=array, dim=2)", - "UBOUND(array=array, dim=2)")]) + (":", ":", "LBOUND(array, dim=1)", + "UBOUND(array, dim=1)", + "LBOUND(array, dim=2)", + "UBOUND(array, dim=2)")]) def test_apply(idim1, idim2, rdim11, rdim12, rdim21, rdim22, fortran_reader, fortran_writer, tmpdir): '''Test that a maxval intrinsic as the only term on the rhs of an @@ -283,7 +283,7 @@ def test_apply(idim1, idim2, rdim11, rdim12, rdim21, rdim22, f" result = maxval(array)\n" f"end subroutine\n") expected = ( - f" result = -HUGE(x=result)\n" + f" result = -HUGE(result)\n" f" do idx = {rdim21}, {rdim22}, 1\n" f" do idx_1 = {rdim11}, {rdim12}, 1\n" f" result = MAX(result, array(idx_1,idx))\n" @@ -316,7 +316,7 @@ def test_apply_multi(fortran_reader, fortran_writer, tmpdir): " result = value1 + maxval(array) * value2\n" "end subroutine\n") expected = ( - " result = -HUGE(x=result)\n" + " result = -HUGE(result)\n" " do idx = 1, m, 1\n" " do idx_1 = 1, n, 1\n" " result = MAX(result, array(idx_1,idx))\n" @@ -370,10 +370,10 @@ def test_mask(fortran_reader, fortran_writer, tmpdir): " result = maxval(array, mask=MOD(array, 2.0)==1)\n" "end program\n") expected = ( - " result = -HUGE(x=result)\n" + " result = -HUGE(result)\n" " do idx = 1, 10, 1\n" " do idx_1 = 1, 10, 1\n" - " if (MOD(a=array(idx_1,idx), p=2.0) == 1) then\n" + " if (MOD(array(idx_1,idx), 2.0) == 1) then\n" " result = MAX(result, array(idx_1,idx))\n" " end if\n" " enddo\n" @@ -406,7 +406,7 @@ def test_mask_array_indexed(fortran_reader, fortran_writer, tmpdir): " result = maxval(a, mask=a(1)>a)\n" "end program\n") expected = ( - " result = -HUGE(x=result)\n" + " result = -HUGE(result)\n" " do idx = 1, 4, 1\n" " if (a(1) > a(idx)) then\n" " result = MAX(result, a(idx))\n" @@ -439,10 +439,10 @@ def test_allocate(fortran_reader, fortran_writer, tmpdir): "end program\n") expected = ( " ALLOCATE(a(1:4,1:4,1:4))\n" - " result = -HUGE(x=result)\n" - " do idx = LBOUND(array=a, dim=3), UBOUND(array=a, dim=3), 1\n" - " do idx_1 = LBOUND(array=a, dim=2), UBOUND(array=a, dim=2), 1\n" - " do idx_2 = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" + " result = -HUGE(result)\n" + " do idx = LBOUND(a, dim=3), UBOUND(a, dim=3), 1\n" + " do idx_1 = LBOUND(a, dim=2), UBOUND(a, dim=2), 1\n" + " do idx_2 = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" " result = MAX(result, a(idx_2,idx_1,idx))\n" " enddo\n" " enddo\n" @@ -473,11 +473,11 @@ def test_references(fortran_reader, fortran_writer, tmpdir): "zmax(1) = MAXVAL(ABS(sshn + ssh_ref * tmask), mask=tmask==1.0)\n" "end subroutine\n") expected = ( - " zmax(1) = -HUGE(x=zmax(1))\n" + " zmax(1) = -HUGE(zmax(1))\n" " do idx = 1, 10, 1\n" " do idx_1 = 1, 10, 1\n" " if (tmask(idx_1,idx) == 1.0) then\n" - " zmax(1) = MAX(zmax(1), ABS(a=sshn(idx_1,idx) + ssh_ref * " + " zmax(1) = MAX(zmax(1), ABS(sshn(idx_1,idx) + ssh_ref * " "tmask(idx_1,idx)))\n" " end if\n" " enddo\n" @@ -502,11 +502,11 @@ def test_nemo_example(fortran_reader, fortran_writer, tmpdir): "zmax(1) = MAXVAL(ABS(sshn(:,:) + ssh_ref * tmask(:,:,1)))\n" "end subroutine\n") expected = ( - " zmax(1) = -HUGE(x=zmax(1))\n" - " do idx = LBOUND(array=sshn, dim=2), UBOUND(array=sshn, dim=2), 1\n" - " do idx_1 = LBOUND(array=sshn, dim=1), " - "UBOUND(array=sshn, dim=1), 1\n" - " zmax(1) = MAX(zmax(1), ABS(a=sshn(idx_1,idx) + ssh_ref * " + " zmax(1) = -HUGE(zmax(1))\n" + " do idx = LBOUND(sshn, dim=2), UBOUND(sshn, dim=2), 1\n" + " do idx_1 = LBOUND(sshn, dim=1), " + "UBOUND(sshn, dim=1), 1\n" + " zmax(1) = MAX(zmax(1), ABS(sshn(idx_1,idx) + ssh_ref * " "tmask(idx_1,idx,1)))\n" " enddo\n" " enddo\n") @@ -532,8 +532,8 @@ def test_constant_dims(fortran_reader, fortran_writer, tmpdir): "x = maxval(a(:,1)+b(10,:), mask=c(:)==1.0)\n" "end subroutine\n") expected = ( - " x = -HUGE(x=x)\n" - " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" + " x = -HUGE(x)\n" + " do idx = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" " if (c(idx) == 1.0) then\n" " x = MAX(x, a(idx,1) + b(10,idx))\n" " end if\n" @@ -565,8 +565,8 @@ def test_expression_1d(fortran_reader, fortran_writer, tmpdir): " real, dimension(10) :: b\n" " real :: x\n" " integer :: idx\n\n" - " x = -HUGE(x=x)\n" - " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" + " x = -HUGE(x)\n" + " do idx = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" " x = MAX(x, a(idx) + b(idx))\n" " enddo\n\n" "end subroutine test\n") @@ -598,11 +598,11 @@ def test_expression_3d(fortran_reader, fortran_writer, tmpdir): " integer :: idx\n" " integer :: idx_1\n" " integer :: idx_2\n\n" - " x = -HUGE(x=x)\n" - " do idx = LBOUND(array=a, dim=3), UBOUND(array=a, dim=3), 1\n" - " do idx_1 = LBOUND(array=a, dim=2), UBOUND(array=a, dim=2), 1\n" - " do idx_2 = LBOUND(array=a, dim=1), " - "UBOUND(array=a, dim=1), 1\n" + " x = -HUGE(x)\n" + " do idx = LBOUND(a, dim=3), UBOUND(a, dim=3), 1\n" + " do idx_1 = LBOUND(a, dim=2), UBOUND(a, dim=2), 1\n" + " do idx_2 = LBOUND(a, dim=1), " + "UBOUND(a, dim=1), 1\n" " x = MAX(x, -a(idx_2,idx_1,idx) + 10.0)\n" " enddo\n" " enddo\n" @@ -630,11 +630,11 @@ def test_multi_intrinsics(fortran_reader, fortran_writer, tmpdir): "x = maxval(a(:)) + maxval(b(:))\n" "end subroutine\n") expected = ( - " x = -HUGE(x=x)\n" - " do idx = LBOUND(array=a, dim=1), UBOUND(array=a, dim=1), 1\n" + " x = -HUGE(x)\n" + " do idx = LBOUND(a, dim=1), UBOUND(a, dim=1), 1\n" " x = MAX(x, a(idx))\n" " enddo\n" - " x = x + MAXVAL(array=b(:))\n") + " x = x + MAXVAL(b(:))\n") psyir = fortran_reader.psyir_from_source(code) trans = Maxval2LoopTrans() # FileContainer/Routine/Assignment/BinaryOp/IntrinsicCall @@ -657,7 +657,7 @@ def test_increment(fortran_reader, fortran_writer, tmpdir): "x = x + maxval(a)\n" "end subroutine\n") expected = ( - " tmp_var = -HUGE(x=tmp_var)\n" + " tmp_var = -HUGE(tmp_var)\n" " do idx = 1, 10, 1\n" " tmp_var = MAX(tmp_var, a(idx))\n" " enddo\n" @@ -685,7 +685,7 @@ def test_increment_with_accessor(fortran_reader, fortran_writer, tmpdir): "end subroutine\n") expected_decl = "real :: tmp_var" expected = ( - " tmp_var = -HUGE(x=tmp_var)\n" + " tmp_var = -HUGE(tmp_var)\n" " do idx = 1, 10, 1\n" " tmp_var = MAX(tmp_var, a(idx))\n" " enddo\n" @@ -712,7 +712,7 @@ def test_reduce_to_struct_and_array_accessors(fortran_reader, fortran_writer): "mystruct%x(3) = maxval(a)\n" "end subroutine\n") expected = ( - " mystruct%x(3) = -HUGE(x=mystruct%x(3))\n" + " mystruct%x(3) = -HUGE(mystruct%x(3))\n" " do idx = 1, 10, 1\n" " mystruct%x(3) = MAX(mystruct%x(3), a(idx))\n" " enddo\n") diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py index 0ad9ca3536..d6246381d2 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/dotproduct2code_trans_test.py @@ -146,8 +146,8 @@ def test_validate_references_matmul(fortran_reader): expected = ( "The DotProduct2CodeTrans transformation only supports the " "transformation of a dotproduct intrinsic if its arguments " - "are plain arrays, but found MATMUL(matrix_a=a3, matrix_b=v1) in " - "DOT_PRODUCT(vector_a=MATMUL(matrix_a=a3, matrix_b=v1), vector_b=v2)") + "are plain arrays, but found MATMUL(a3, v1) in " + "DOT_PRODUCT(MATMUL(a3, v1), v2)") check_validate(code, expected, fortran_reader) @@ -171,8 +171,8 @@ def test_validate_references_structure(fortran_reader): expected = ( "The DotProduct2CodeTrans transformation only supports the " "transformation of a dotproduct intrinsic if its arguments are plain " - "arrays, but found grid%var1(:) in DOT_PRODUCT(vector_a=grid%var1(:)" - ", vector_b=grid%var2(:)).") + "arrays, but found grid%var1(:) in DOT_PRODUCT(grid%var1(:)" + ", grid%var2(:)).") check_validate(code, expected, fortran_reader) @@ -192,8 +192,8 @@ def test_validate_1d_array(fortran_reader): "The DotProduct2CodeTrans transformation only supports the " "transformation of a dotproduct intrinsic with an argument not " "containing an array slice if the argument is a 1D array, but " - "found a1 with 2 dimensions in DOT_PRODUCT(vector_a=a1, " - "vector_b=a2).") + "found a1 with 2 dimensions in DOT_PRODUCT(a1, " + "a2).") check_validate(code, expected, fortran_reader) @@ -215,7 +215,7 @@ def test_validate_array_slice_dim1(fortran_reader): "transformation of a dotproduct intrinsic with an argument " "containing an array slice if the array slice is for the 1st " "dimension of the array, but found a2(1,:) in " - "DOT_PRODUCT(vector_a=a1(:,1), vector_b=a2(1,:)).") + "DOT_PRODUCT(a1(:,1), a2(1,:)).") check_validate(code, expected, fortran_reader) @@ -237,7 +237,7 @@ def test_validate_array_full_slice(fortran_reader): "transformation of a dotproduct intrinsic with an argument containing " "an array slice if the argument is for the 1st dimension of the array " "and is for the full range of that dimension, but found a1(2:4,1) in " - "DOT_PRODUCT(vector_a=a1(2:4,1), vector_b=a2(:,10)).") + "DOT_PRODUCT(a1(2:4,1), a2(:,10)).") check_validate(code, expected, fortran_reader) @@ -255,8 +255,8 @@ def test_validate_real(fortran_reader): "end subroutine\n") expected = ( "The DotProduct2CodeTrans transformation only supports arrays of " - "real data, but found v1 of type INTEGER in DOT_PRODUCT(vector_a=v1, " - "vector_b=v2).") + "real data, but found v1 of type INTEGER in DOT_PRODUCT(v1, " + "v2).") check_validate(code, expected, fortran_reader) @@ -322,7 +322,7 @@ def test_apply_unknown_dims(tmpdir, fortran_reader, fortran_writer): " integer :: i\n" " real(kind=r_def) :: res_dot_product\n\n" " res_dot_product = 0.0\n" - " do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" + " do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" " res_dot_product = res_dot_product + v1(i) * v2(i)\n" " enddo\n" " result = res_dot_product\n\n") @@ -371,7 +371,7 @@ def test_apply_array_notation( " integer :: i\n" " real :: res_dot_product\n\n" " res_dot_product = 0.0\n" - " do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" + " do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" " res_dot_product = res_dot_product + v1(i) * v2(i)\n" " enddo\n" " result = res_dot_product\n\n") @@ -399,7 +399,7 @@ def test_apply_extra_dims(tmpdir, fortran_reader, fortran_writer, arg1, arg2, f" integer :: i\n" f" real :: res_dot_product\n\n" f" res_dot_product = 0.0\n" - f" do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" + f" do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" f" res_dot_product = res_dot_product + v1{res1} * v2{res2}\n" f" enddo\n" f" result = res_dot_product\n\n") @@ -428,7 +428,7 @@ def test_apply_extra_dims_sizes(tmpdir, fortran_reader, fortran_writer, f" integer :: i\n" f" real :: res_dot_product\n\n" f" res_dot_product = 0.0\n" - f" do i = LBOUND(array=v1, dim=1), UBOUND(array=v1, dim=1), 1\n" + f" do i = LBOUND(v1, dim=1), UBOUND(v1, dim=1), 1\n" f" res_dot_product = res_dot_product + v1{res1} * v2{res2}\n" f" enddo\n" f" result = res_dot_product\n\n") diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py index fc3a791fa5..38ae0db73b 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/matmul2code_trans_test.py @@ -258,8 +258,8 @@ def test_validate_arg_not_ref(): with pytest.raises(TransformationError) as excinfo: trans.validate(matmul) assert ("Expected result and operands of MATMUL IntrinsicCall to be " - "references, but found: 'x(10) = MATMUL(matrix_a=x(10) * x(10), " - "matrix_b=x(10) * x(10))\n'." in str(excinfo.value)) + "references, but found: 'x(10) = MATMUL(x(10) * x(10), " + "x(10) * x(10))\n'." in str(excinfo.value)) def test_validate_arg_not_arr(): diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py index 52223e4954..f61b54b139 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/maxval2loop_trans_test.py @@ -70,7 +70,7 @@ def test_init_var(): trans = Maxval2LoopTrans() var_symbol = DataSymbol("var", REAL_TYPE) result = trans._init_var(Reference(var_symbol)) - assert result.debug_string() == "-HUGE(x=var)" + assert result.debug_string() == "-HUGE(var)" def test_str(): @@ -125,7 +125,7 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): " real :: result\n" " integer :: idx\n" " integer :: idx_1\n\n" - " result = -HUGE(x=result)\n" + " result = -HUGE(result)\n" " do idx = 1, 20, 1\n" " do idx_1 = 1, 10, 1\n" " result = MAX(result, array(idx_1,idx))\n" diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py index 6710ec45a1..bf0225d991 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/minval2loop_trans_test.py @@ -68,7 +68,7 @@ def test_init_var(): trans = Minval2LoopTrans() var_symbol = DataSymbol("var", REAL_TYPE) result = trans._init_var(Reference(var_symbol)) - assert result.debug_string() == "HUGE(x=var)\n" + assert result.debug_string() == "HUGE(var)\n" def test_str(): @@ -116,7 +116,7 @@ def test_apply(fortran_reader, fortran_writer, tmpdir): " result = minval(array)\n" "end subroutine\n") expected = ( - " result = HUGE(x=result)\n" + " result = HUGE(result)\n" " do idx = 1, 20, 1\n" " do idx_1 = 1, 10, 1\n" " result = MIN(result, array(idx_1,idx))\n" diff --git a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py index f10d07a576..03b2761592 100644 --- a/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/intrinsics/sign2code_trans_test.py @@ -110,7 +110,7 @@ def test_correct(func, output, tmpdir): f" real, intent(inout) :: arg\n" f" real, intent(inout) :: arg_1\n" f" real :: psyir_tmp\n\n" - f" psyir_tmp = SIGN(a={output}, b=arg_1)\n\n" + f" psyir_tmp = SIGN({output}, arg_1)\n\n" f"end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call) @@ -164,7 +164,7 @@ def test_correct_expr(tmpdir): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = 1.0 + SIGN(a=arg * 3.14, b=arg_1) + 2.0\n\n" + " psyir_tmp = 1.0 + SIGN(arg * 3.14, arg_1) + 2.0\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call) @@ -218,7 +218,7 @@ def test_correct_2sign(tmpdir, fortran_writer): " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" - " psyir_tmp = SIGN(a=1.0, b=1.0) + SIGN(a=arg * 3.14, b=arg_1)\n\n" + " psyir_tmp = SIGN(1.0, 1.0) + SIGN(arg * 3.14, arg_1)\n\n" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(intr_call) @@ -311,8 +311,8 @@ def test_sign_of_unknown_type(fortran_reader): if call.intrinsic.name == "SIGN"] with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[0]) - assert ("Sign2CodeTrans cannot be applied to 'SIGN(a=MAX(ABS(a=ztmp1), " - "1.e-6_wp), b=ztmp1) because the type of the argument" + assert ("Sign2CodeTrans cannot be applied to 'SIGN(MAX(ABS(ztmp1), " + "1.e-6_wp), ztmp1) because the type of the argument" in str(err.value)) with pytest.raises(TransformationError) as err: trans.validate(sgn_calls[1]) diff --git a/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py b/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py index c2a0d635ac..a34ea7e113 100644 --- a/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py +++ b/src/psyclone/tests/psyir/transformations/omp_task_transformations_test.py @@ -132,8 +132,8 @@ def test_omptask_apply(fortran_reader, fortran_writer): do jj = 1, 10, 1 !$omp task private(ji) firstprivate(jj) shared(t,s) \ depend(in: s(:,jj)) depend(out: t(:,jj)) - do ji = 1, SIZE(array=ji, dim=2), 1 - t(ji,jj) = INT(a=s(ji,jj)) + do ji = 1, SIZE(ji, dim=2), 1 + t(ji,jj) = INT(s(ji,jj)) enddo !$omp end task enddo diff --git a/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py b/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py index 2eb126fb5f..f63d622a01 100644 --- a/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/reference2arrayrange_trans_test.py @@ -165,7 +165,7 @@ def test_intrinsics(fortran_reader, fortran_writer): ''' code = CODE.replace("a = b", "b = dot_product(a, a(:))") result = apply_trans(fortran_reader, fortran_writer, code) - assert "b = DOT_PRODUCT(vector_a=a, vector_b=a(:))" in result + assert "b = DOT_PRODUCT(a, a(:))" in result def test_call(fortran_reader, fortran_writer): @@ -261,7 +261,7 @@ def test_validate_query(fortran_reader): with pytest.raises(TransformationError) as info: trans.validate(reference) assert (f"supplied node is passed as an argument to a Call to a " - f"non-elemental routine ({text}(array=a, dim=1)) and " + f"non-elemental routine ({text}(a, dim=1)) and " f"should not be transformed." in str(info.value)) # Check the references to 'b' in the hidden lbound and ubound diff --git a/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py b/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py index 46292eff24..4e7cd9aa7a 100644 --- a/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/replace_induction_variables_trans_test.py @@ -122,14 +122,14 @@ def test_riv_working(fortran_reader, fortran_writer): "(i + 1 + invariant)" in out_loop) assert "ic4 = i - 1 + 1 + invariant" in out_all - assert ("a(i * i + 3 * SIN(x=i)) = 5 + (i * i + 3 * SIN(x=i) + 1) * " - "(i * i + 3 * SIN(x=i))" in out_loop) - assert "ic5 = (i - 1) * (i - 1) + 3 * SIN(x=i - 1)" in out_all - assert ("a(i + 1 + invariant + (i * i + 3 * SIN(x=i))) = 6 + " - "(i + 1 + invariant + (i * i + 3 * SIN(x=i)) + 1) * " - "(i + 1 + invariant + (i * i + 3 * SIN(x=i)))" in out_loop) + assert ("a(i * i + 3 * SIN(i)) = 5 + (i * i + 3 * SIN(i) + 1) * " + "(i * i + 3 * SIN(i))" in out_loop) + assert "ic5 = (i - 1) * (i - 1) + 3 * SIN(i - 1)" in out_all + assert ("a(i + 1 + invariant + (i * i + 3 * SIN(i))) = 6 + " + "(i + 1 + invariant + (i * i + 3 * SIN(i)) + 1) * " + "(i + 1 + invariant + (i * i + 3 * SIN(i)))" in out_loop) assert ("ic6 = i - 1 + 1 + invariant + ((i - 1) * (i - 1) + " - "3 * SIN(x=i - 1))" in out_all) + "3 * SIN(i - 1))" in out_all) assert "a(13) = 7 + (13 + 1) * 13" in out_loop # Make sure the assignment to t1%a has been added outside of the loop: assert "t1%a = 13" in out_all @@ -255,6 +255,6 @@ def test_riv_impure_function_calls(fortran_reader, fortran_writer): out = fortran_writer(loop) # ic1 has been replaced - assert "a(SIN(x=i)) = 1 + (SIN(x=i) + 1) * SIN(x=i)" in out + assert "a(SIN(i)) = 1 + (SIN(i) + 1) * SIN(i)" in out # ic2 has NOT been replaced assert "a(ic2) = 1 + (ic2 + 1) * ic2" in out diff --git a/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py b/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py index 565f54bca9..5420028448 100644 --- a/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/scalarisation_trans_test.py @@ -625,7 +625,7 @@ def test_scalarisation_trans_apply(fortran_reader, fortran_writer, tmpdir): do i = 1, 100, 1 arr_scalar = i - arr_scalar = EXP(x=arr_scalar) + arr_scalar = EXP(arr_scalar) k = i b(i) = arr_scalar * 3 c(k) = i @@ -648,7 +648,7 @@ def test_scalarisation_trans_apply(fortran_reader, fortran_writer, tmpdir): do i = 1, 100 arr(i) = i - arr(i) = exp(x=arr(i)) + arr(i) = exp(arr(i)) k = i b(i) = arr(i) * 3 c(k) = i @@ -677,7 +677,7 @@ def test_scalarisation_trans_apply(fortran_reader, fortran_writer, tmpdir): do i = 1, 100, 1 arr_scalar = i - arr_scalar = EXP(x=arr_scalar) + arr_scalar = EXP(arr_scalar) k = i b(i) = arr_scalar * 3 c(k) = i @@ -703,7 +703,7 @@ def test_scalarisation_trans_apply_routinesymbol(fortran_reader, integer :: i integer, allocatable, dimension(:,:,:) :: k do i= 1, 100 - allocate(k(MAXVAL(array=j(1:3)),1,1)) + allocate(k(MAXVAL(j(1:3)),1,1)) deallocate(k) end do end subroutine test''' @@ -716,7 +716,7 @@ def test_scalarisation_trans_apply_routinesymbol(fortran_reader, integer, allocatable, dimension(:,:,:) :: k do i = 1, 100, 1 - ALLOCATE(k(1:MAXVAL(array=j(:)),1:1,1:1)) + ALLOCATE(k(1:MAXVAL(j(:)),1:1,1:1)) DEALLOCATE(k) enddo From 5e6ec47623efa2ff0bb785bb8e57146ff263b042 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 20 Nov 2025 11:03:15 +0000 Subject: [PATCH 42/50] Fixed formatting --- src/psyclone/psyir/backend/sympy_writer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index b9292c9cab..3d25b752ef 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -802,7 +802,8 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: return f"{self._nindent}{node.routine.name}({args})\n" return f"{node.routine.name}({args})" if not node.parent or isinstance(node.parent, Schedule): - return f"{self._nindent}call {self._visit(node.routine)}({args})\n" + return (f"{self._nindent}call " + f"{self._visit(node.routine)}({args})\n") # Otherwise it is inside-expression function call return f"{self._visit(node.routine)}({args})" From 4cfefabead0fc5cb7c78767dca78fed656d3434a Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 20 Nov 2025 14:11:02 +0000 Subject: [PATCH 43/50] Added intrinsicsymbol copy and fixed uncovered/unneeded code --- src/psyclone/psyir/backend/fortran.py | 3 - src/psyclone/psyir/backend/sympy_writer.py | 4 -- .../psyir/symbols/intrinsic_symbol.py | 19 ++++++ .../tests/psyir/backend/sympy_writer_test.py | 29 ++++++++- .../psyir/symbols/intrinsicsymbol_test.py | 64 +++++++++++++++++++ 5 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index 3d7b4e5b7b..d118809b59 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1799,9 +1799,6 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: if not node.parent or isinstance(node.parent, Schedule): return f"{self._nindent}call {self._visit(node.routine)}({args})\n" - # Otherwise it is inside-expression function call - return f"{self._visit(node.routine)}({args})" - def call_node(self, node: Call) -> str: '''Translate the PSyIR call node to Fortran. diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index 51f0a81cae..ad1c9e95bf 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -619,7 +619,6 @@ def __call__( result = [] for expr in expression_str_list: - print(expr) try: result.append(parse_expr(expr, self.type_map)) except SyntaxError as err: @@ -805,9 +804,6 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: return (f"{self._nindent}call " f"{self._visit(node.routine)}({args})\n") - # Otherwise it is inside-expression function call - return f"{self._visit(node.routine)}({args})" - # ------------------------------------------------------------------------- def reference_node(self, node: Reference) -> str: '''This method is called when a Reference instance is found in the diff --git a/src/psyclone/psyir/symbols/intrinsic_symbol.py b/src/psyclone/psyir/symbols/intrinsic_symbol.py index d5592c63df..1fd9266ece 100644 --- a/src/psyclone/psyir/symbols/intrinsic_symbol.py +++ b/src/psyclone/psyir/symbols/intrinsic_symbol.py @@ -66,6 +66,25 @@ def intrinsic(self): ''' return self._intrinsic + def copy(self): + '''Create and return a copy of this object. Any references to the + original will not be affected so the copy will not be referred + to by any other object. + + :returns: A symbol object with the same properties as this + symbol object. + :rtype: :py:class:`psyclone.psyir.symbols.IntrinsicSymbol` + + ''' + # The constructors for all Symbol-based classes have 'name' as the + # first positional argument. + return type(self)(self.name, self.intrinsic, + datatype=self.datatype.copy(), + visibility=self.visibility, + interface=self.interface.copy(), + is_pure=self.is_pure, + is_elemental=self.is_elemental) + # For Sphinx AutoAPI documentation generation __all__ = ["IntrinsicSymbol"] diff --git a/src/psyclone/tests/psyir/backend/sympy_writer_test.py b/src/psyclone/tests/psyir/backend/sympy_writer_test.py index 3cc4b875c3..88d2e6e1ea 100644 --- a/src/psyclone/tests/psyir/backend/sympy_writer_test.py +++ b/src/psyclone/tests/psyir/backend/sympy_writer_test.py @@ -48,10 +48,10 @@ from psyclone.psyir.backend.sympy_writer import SymPyWriter from psyclone.psyir.backend.visitor import VisitorError from psyclone.psyir.nodes import ( - Assignment, Literal, Node, IntrinsicCall, Reference + Assignment, Literal, Node, IntrinsicCall, Reference, Call ) from psyclone.psyir.symbols import (ArrayType, BOOLEAN_TYPE, CHARACTER_TYPE, - INTEGER_TYPE) + INTEGER_TYPE, SymbolTable) def test_sym_writer_constructor(): @@ -671,3 +671,28 @@ def test_sym_writer_identical_variables_errors(): sympy_writer(Node(), identical_variables={"var": 1}) assert ("Dictionary identical_variables contains a non-string key or " "value" in str(err.value)) + + +def test_sym_writer_intrinsiccall_node(fortran_reader): + '''Handle unlikely edge cases for intrinsiccall node.''' + code = """subroutine test + integer :: b + call randomcall(b, b, b, b, b) + end subroutine test""" + # Can't create MVBITS intrinsic directly yet - create + # a call then replace it. + psyir = fortran_reader.psyir_from_source(code) + call = psyir.walk(Call)[0] + args = [] + for arg in call.arguments[:]: + args.append(arg.detach()) + intrinsic = IntrinsicCall.create( + IntrinsicCall.Intrinsic.MVBITS, + args + ) + call.replace_with(intrinsic) + sympy_writer = SymPyWriter() + # Add an arbitrary symbol table to avoid things breaking. + sympy_writer._symbol_table = SymbolTable() + res = sympy_writer.intrinsiccall_node(psyir.walk(IntrinsicCall)[0]) + assert "call MVBITS(b, b, b, b, b)\n" == res diff --git a/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py b/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py new file mode 100644 index 0000000000..4a62665f60 --- /dev/null +++ b/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py @@ -0,0 +1,64 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2020-2025, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# Author A. B. G. Chalk, STFC Daresbury Lab +# ----------------------------------------------------------------------------- + +''' Perform py.test tests on the psygen.psyir.symbols.intrinsicsymbol file ''' + +import pytest + +from psyclone.psyir.nodes import IntrinsicCall +from psyclone.psyir.symbols import IntrinsicSymbol + + +def test_intrinsicsymbol_copy(fortran_reader): + '''Test the copy function on the IntrinsicSymbol class. + ''' + # Create an IntrinsicCall + code = """subroutine x + integer :: a + a = INT(1.0) + end subroutine x""" + psyir = fortran_reader.psyir_from_source(code) + intrinsic = psyir.walk(IntrinsicCall)[0] + assert isinstance(intrinsic.routine.symbol, IntrinsicSymbol) + isym = intrinsic.routine.symbol + copy = isym.copy() + assert isym.name == copy.name + assert isym.intrinsic == copy.intrinsic + assert isym.datatype == copy.datatype + assert isym.visibility == copy.visibility + assert isym.interface == copy.interface + assert isym.is_pure == copy.is_pure + assert isym.is_elemental == copy.is_elemental From b8d702c33b4aabe0634ddd6dc44938178db5537d Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 20 Nov 2025 14:13:42 +0000 Subject: [PATCH 44/50] linting] --- src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py b/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py index 4a62665f60..67a67160f2 100644 --- a/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py +++ b/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py @@ -36,8 +36,6 @@ ''' Perform py.test tests on the psygen.psyir.symbols.intrinsicsymbol file ''' -import pytest - from psyclone.psyir.nodes import IntrinsicCall from psyclone.psyir.symbols import IntrinsicSymbol From c6b346649f78867f7b8ac664b9e40f9dd3a88597 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 20 Nov 2025 14:45:45 +0000 Subject: [PATCH 45/50] Fixed issue with an intrinsic with capitalised intrinsic argument names in its definition --- src/psyclone/psyir/nodes/intrinsic_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index d5c3420c66..c114d45392 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -2691,7 +2691,7 @@ class Intrinsic(IAttr, Enum): max_count=0, types=Reference, arg_names=()), - optional_args={"P": DataNode, "R": DataNode, "radix": DataNode}, + optional_args={"p": DataNode, "r": DataNode, "radix": DataNode}, return_type=None, reference_accesses=None, ) From 28899ef3976d8a9129bb1eb3662c85c1f946e976 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Thu, 20 Nov 2025 15:15:10 +0000 Subject: [PATCH 46/50] Fixed CMPLX --- src/psyclone/psyir/nodes/intrinsic_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/psyir/nodes/intrinsic_call.py b/src/psyclone/psyir/nodes/intrinsic_call.py index c114d45392..db326c988a 100644 --- a/src/psyclone/psyir/nodes/intrinsic_call.py +++ b/src/psyclone/psyir/nodes/intrinsic_call.py @@ -807,7 +807,7 @@ class Intrinsic(IAttr, Enum): max_count=1, types=DataNode, arg_names=(("x",),)), - optional_args={"Y": DataNode, "kind": DataNode}, + optional_args={"y": DataNode, "kind": DataNode}, return_type=None, reference_accesses=None, ) From be08a532d79c7f118280d331a4795b85ed97dcaf Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Mon, 24 Nov 2025 16:09:16 +0000 Subject: [PATCH 47/50] Changes for review --- doc/developer_guide/psyir.rst | 13 ++++++++----- src/psyclone/configuration.py | 4 ++-- src/psyclone/generator.py | 14 +++++++------- src/psyclone/psyir/backend/fortran.py | 13 +++++++------ src/psyclone/psyir/backend/sympy_writer.py | 9 +++++---- src/psyclone/psyir/symbols/intrinsic_symbol.py | 5 +++-- .../tests/psyir/backend/sympy_writer_test.py | 3 ++- ...nsicsymbol_test.py => intrinsic_symbol_test.py} | 3 ++- 8 files changed, 36 insertions(+), 28 deletions(-) rename src/psyclone/tests/psyir/symbols/{intrinsicsymbol_test.py => intrinsic_symbol_test.py} (96%) diff --git a/doc/developer_guide/psyir.rst b/doc/developer_guide/psyir.rst index b8873694ae..c0fe5ccc7b 100644 --- a/doc/developer_guide/psyir.rst +++ b/doc/developer_guide/psyir.rst @@ -594,15 +594,18 @@ available PSyIR `IntrinsicCall` match those of the `Fortran 2018 standard In addition to Fortran Intrinsics, special Fortran statements such as: `ALLOCATE`, `DEALLOCATE` and `NULLIFY` are also PSyIR IntrinsicCalls. -``IntrinsicCall`` nodes have a canonicalisation function, that is used +``IntrinsicCall`` nodes have a ``compute_argument_names`` function, that is used within PSyclone during their creation (via the ``IntrinsicCall.create`` function). This attempts to match the Intrinsic and input arguments to one of the interfaces for the intrinsic (as some intrinsics have multiple -possible argument interfaces). If the canonicalisation is successful, PSyclone -will convert all of the arguments to be named arguments. If canonicalisation -fails, then PSyclone will create a ``CodeBlock``. This canonicalisation is +possible argument interfaces). If successful, PSyclone +will convert all of the arguments to be named arguments. If argument name computation +fails, then PSyclone will create a ``CodeBlock``. This computation is required to guarantee correct behaviour when computing reference_accesses -or the return type of an Intrinsic. +or the return type of an Intrinsic. The computation of argument names may change +the output of the intrinsic - optional arguments will always have their argument +names displayed in the Fortran output, whilst required argument names are +not generated by default. IntrinsicCalls, like Calls, have properties to inform if the call is to a pure, elemental, inquiry (does not touch the first argument data) function diff --git a/src/psyclone/configuration.py b/src/psyclone/configuration.py index cd9a0793d8..0647a84dbd 100644 --- a/src/psyclone/configuration.py +++ b/src/psyclone/configuration.py @@ -235,8 +235,8 @@ def __init__(self): # The Fortran standard that fparser should use self._fortran_standard = None - # By default, the PSyIR backends output argument names on (most) - # IntrinsicCalls. This option enables control of that behaviour. + # By default, the PSyIR backends don't output argument names on (most) + # IntrinsicCalls. This option controls that behaviour. self._backend_intrinsic_named_kwargs = False # ------------------------------------------------------------------------- diff --git a/src/psyclone/generator.py b/src/psyclone/generator.py index 5b717c93a2..1ea3dc652c 100644 --- a/src/psyclone/generator.py +++ b/src/psyclone/generator.py @@ -651,9 +651,15 @@ def main(arguments): # Record any intrinsic output format settings. if "backend_add_all_intrinsic_arg_names" in args: - # The backend won't attempt to add names to required + # Tells the backend to attempt to add names to required # arguments to Fortran intrinsics. Config.get().backend_intrinsic_named_kwargs = True + # A command-line flag overrides the setting in the Config file (if + # any). + if "backend_disable_validation" in args: + Config.get().backend_checks_enabled = False + if "backend_disable_indentation" in args: + Config.get().backend_indentation_disabled = True # Record any profiling options. if args.profile: @@ -662,12 +668,6 @@ def main(arguments): except ValueError as err: print(f"Invalid profiling option: {err}", file=sys.stderr) sys.exit(1) - # A command-line flag overrides the setting in the Config file (if - # any). - if "backend_disable_validation" in args: - Config.get().backend_checks_enabled = False - if "backend_disable_indentation" in args: - Config.get().backend_indentation_disabled = True # The Configuration manager checks that the supplied path(s) is/are # valid so protect with a try diff --git a/src/psyclone/psyir/backend/fortran.py b/src/psyclone/psyir/backend/fortran.py index d118809b59..46db9357b0 100644 --- a/src/psyclone/psyir/backend/fortran.py +++ b/src/psyclone/psyir/backend/fortran.py @@ -1754,10 +1754,10 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: # Config says to avoid outputting argument names where # possible. try: - # Canonicalisation handles any error checking we might - # otherwise want to try. Most IntrinsicCalls should already - # have argument names added, but we do it here to ensure that - # it is is possible. + # Argument name computation handles any error checking we + # might otherwise want to try. Most IntrinsicCalls should + # already have argument names added, but we do it here to + # ensure that argument name computation is possible. node.compute_argument_names() intrinsic_interface = node._find_matching_interface() args = [] @@ -1796,8 +1796,9 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: return f"{self._nindent}{node.routine.name}({args})\n" return f"{node.routine.name}({args})" - if not node.parent or isinstance(node.parent, Schedule): - return f"{self._nindent}call {self._visit(node.routine)}({args})\n" + # Otherwise we have one of the intrinsics that requires "Call X" + # syntax. + return f"{self._nindent}call {self._visit(node.routine)}({args})\n" def call_node(self, node: Call) -> str: '''Translate the PSyIR call node to Fortran. diff --git a/src/psyclone/psyir/backend/sympy_writer.py b/src/psyclone/psyir/backend/sympy_writer.py index ad1c9e95bf..7862aada6d 100644 --- a/src/psyclone/psyir/backend/sympy_writer.py +++ b/src/psyclone/psyir/backend/sympy_writer.py @@ -791,8 +791,9 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: except KeyError: # This section is copied from FortranWriter IntrinsicCall, # but doesn't attempt to match argument names and so avoids - # readding optional argument names back in. + # re-adding optional argument names back in. args = self._gen_arguments(node) + # These routines require `call` syntax in Fortran. if node.routine.name not in [ "DATE_AND_TIME", "SYSTEM_CLOCK", "MVBITS", "RANDOM_NUMBER", "RANDOM_SEED"]: @@ -800,9 +801,9 @@ def intrinsiccall_node(self, node: IntrinsicCall) -> str: if not node.parent or isinstance(node.parent, Schedule): return f"{self._nindent}{node.routine.name}({args})\n" return f"{node.routine.name}({args})" - if not node.parent or isinstance(node.parent, Schedule): - return (f"{self._nindent}call " - f"{self._visit(node.routine)}({args})\n") + # Otherwise we have an intrinsic that has call syntax. + return (f"{self._nindent}call " + f"{self._visit(node.routine)}({args})\n") # ------------------------------------------------------------------------- def reference_node(self, node: Reference) -> str: diff --git a/src/psyclone/psyir/symbols/intrinsic_symbol.py b/src/psyclone/psyir/symbols/intrinsic_symbol.py index 1fd9266ece..ce3184f788 100644 --- a/src/psyclone/psyir/symbols/intrinsic_symbol.py +++ b/src/psyclone/psyir/symbols/intrinsic_symbol.py @@ -36,6 +36,8 @@ ''' This module contains the IntrinsicSymbol.''' +from __future__ import annotations + from psyclone.psyir.symbols.routinesymbol import RoutineSymbol @@ -66,14 +68,13 @@ def intrinsic(self): ''' return self._intrinsic - def copy(self): + def copy(self) -> IntrinsicSymbol: '''Create and return a copy of this object. Any references to the original will not be affected so the copy will not be referred to by any other object. :returns: A symbol object with the same properties as this symbol object. - :rtype: :py:class:`psyclone.psyir.symbols.IntrinsicSymbol` ''' # The constructors for all Symbol-based classes have 'name' as the diff --git a/src/psyclone/tests/psyir/backend/sympy_writer_test.py b/src/psyclone/tests/psyir/backend/sympy_writer_test.py index 88d2e6e1ea..f57c606ce7 100644 --- a/src/psyclone/tests/psyir/backend/sympy_writer_test.py +++ b/src/psyclone/tests/psyir/backend/sympy_writer_test.py @@ -674,7 +674,8 @@ def test_sym_writer_identical_variables_errors(): def test_sym_writer_intrinsiccall_node(fortran_reader): - '''Handle unlikely edge cases for intrinsiccall node.''' + '''Handle edge cases for intrinsiccall node, i.e. when we have an + IntrinsicCall input that requires a call declaration (e.g. MVBITS).''' code = """subroutine test integer :: b call randomcall(b, b, b, b, b) diff --git a/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py b/src/psyclone/tests/psyir/symbols/intrinsic_symbol_test.py similarity index 96% rename from src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py rename to src/psyclone/tests/psyir/symbols/intrinsic_symbol_test.py index 67a67160f2..62486f825e 100644 --- a/src/psyclone/tests/psyir/symbols/intrinsicsymbol_test.py +++ b/src/psyclone/tests/psyir/symbols/intrinsic_symbol_test.py @@ -34,7 +34,7 @@ # Author A. B. G. Chalk, STFC Daresbury Lab # ----------------------------------------------------------------------------- -''' Perform py.test tests on the psygen.psyir.symbols.intrinsicsymbol file ''' +''' Perform py.test tests on the psygen.psyir.symbols.intrinsic_symbol file ''' from psyclone.psyir.nodes import IntrinsicCall from psyclone.psyir.symbols import IntrinsicSymbol @@ -53,6 +53,7 @@ def test_intrinsicsymbol_copy(fortran_reader): assert isinstance(intrinsic.routine.symbol, IntrinsicSymbol) isym = intrinsic.routine.symbol copy = isym.copy() + assert copy is not isym assert isym.name == copy.name assert isym.intrinsic == copy.intrinsic assert isym.datatype == copy.datatype From 131ec43be2a1baccfa212c4f0c2ebd89ea9d7a61 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 28 Nov 2025 13:53:59 +0000 Subject: [PATCH 48/50] Comment updates for new functionality --- doc/user_guide/psyclone_command.rst | 28 ++++++++++--------- src/psyclone/psyir/frontend/fparser2.py | 26 +++++++++-------- .../transformations/parallel_loop_trans.py | 2 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/doc/user_guide/psyclone_command.rst b/doc/user_guide/psyclone_command.rst index 4042ffb877..cf4be8876b 100644 --- a/doc/user_guide/psyclone_command.rst +++ b/doc/user_guide/psyclone_command.rst @@ -60,7 +60,8 @@ by the command: [--log-level {OFF,DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-file LOG_FILE] [--keep-comments] [--keep-directives] [-I INCLUDE] [-d DIRECTORY] [--modman-file-ignore IGNORE_PATTERN] [--free-form | --fixed-form] - [--backend {disable-validation,disable-indentation}] [--disable-named-intrinsic-args] + [--backend-disable-validation] [--backend-disable-indentation] + [--backend-add-all-intrinsic-arg-names] filename Transform a file using the PSyclone source-to-source Fortran compiler @@ -122,14 +123,14 @@ by the command: Fortran backend control options.: These settings control how PSyclone outputs Fortran. - --backend {disable-validation,disable-indentation} - options to control the PSyIR backend used for code generation. - Use 'disable-validation' to disable the validation checks that are performed by - default. Use 'disable-indentation' to turn off all indentation in the generated code. - --disable-named-intrinsic-args - By default, the backend names any required arguments to intrinsic calls. This option - disables this feature (in case the processed code has overridden a Fortran intrinsic), - i.e. SUM(arr, mask=maskarr) instead of SUM(array=arr, mask=maskarr). + --backend-disable-validation + Disables validation checks that PSyclone backends perform by default. + --backend-disable-indentation + Disables all indentation in the generated output code. + --backend-add-all-intrinsic-arg-names + By default, the backend outputs the names of only optional arguments to intrinsic calls. + This option enables all argument names on intrinsic + calls, i.e. SUM(array=arr, mask=maskarr) instead of SUM(arr, mask=maskarr). Basic Use --------- @@ -328,11 +329,12 @@ command-line setting always takes precedence. Overriding Fortran Intrinsics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -PSyclone attempts to canonicalise Fortran Intrinsics, which involves adding -argument names to each argument in the ``IntrinsicCall`` PSyIR node. This can +PSyclone internally computes the argument names for each argument provided to +a Fortran IntrinsicCall PSyIR node. This can cause problems with code that overrides Fortran intrinsics. To ensure correct -behaviour of the output, the ``--disable-named-intrinsic-args`` option must -be passed to PSyclone, else the resultant code may not compile or run correctly. +behaviour of the output, the ``--backend-add-all-intrinsic-arg-names`` option +must **not** be passed to PSyclone, else the resultant code may not compile or +run correctly. Automatic Profiling Instrumentation ----------------------------------- diff --git a/src/psyclone/psyir/frontend/fparser2.py b/src/psyclone/psyir/frontend/fparser2.py index a3ecc88f94..d8e9eb1818 100644 --- a/src/psyclone/psyir/frontend/fparser2.py +++ b/src/psyclone/psyir/frontend/fparser2.py @@ -3016,7 +3016,8 @@ def _allocate_handler(self, node, parent): (e.g. allocate(character(len=10) :: my_var)). ''' - # Canonicalise doesn't do anything for ALLOCATE so we don't need to. + # Computing argument names doesn't do anything for ALLOCATE so we + # don't call it. call = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATE, parent=parent) type_spec = node.children[0] @@ -3172,7 +3173,8 @@ def _deallocate_handler(self, node, parent): :rtype: :py:class:`psyclone.psyir.nodes.IntrinsicCall` ''' - # Canonicalise doesn't do anything for DEALLOCATE so we don't need to. + # Computing argument names doesn't do anything for DEALLOCATE so we + # don't call it. call = IntrinsicCall( IntrinsicCall.Intrinsic.DEALLOCATE, parent=parent) dealloc_list = node.children[0].children @@ -5208,7 +5210,7 @@ def _process_args(self, node: Union[ Fortran2003.Intrinsic_Function_Reference ], call: Union[Call, IntrinsicCall], - canonicalise: bool = False) -> Union[ + check_valid_argument_ordering: bool = False) -> Union[ Call, IntrinsicCall]: '''Processes fparser2 call or intrinsic arguments contained in the node argument and adds them to the PSyIR Call or IntrinsicCall @@ -5230,8 +5232,10 @@ def _process_args(self, node: Union[ an intrinsic call. :param call: a PSyIR call argument representing a call or an intrinsic call. - :param canonicalise: whether to canonicalise the call (for - IntrinsicCalls). + :param check_valid_argument_ordering: whether to check the order + of the arguments provided to the call are valid (currently just for + IntrinsicCalls). This check ensures that all unnamed arguments + appear before any named arguments. :returns: the PSyIR call argument with the PSyIR representation of the fparser2 node arguments. @@ -5248,13 +5252,11 @@ def _process_args(self, node: Union[ # Sanity check that all named arguments follow all positional # arguments. This should be the case but fparser does not - # currently check and this ordering is assumed by the - # canonicalise function. LFRic invokes can cause this - # exception (as they often use name=xxx before the end of the - # argument list), so to avoid this we only check when a - # canonicalise function is supplied (which we know is not the - # case for invokes as they are calls). - if canonicalise: + # currently check and this ordering is assumed. LFRic invokes can + # cause this exception (as they often use name=xxx before the end of + # the argument list), so to avoid this we only check when the option + # is enabled. + if check_valid_argument_ordering: index = 0 while index < len(arg_names) and not arg_names[index]: index += 1 diff --git a/src/psyclone/psyir/transformations/parallel_loop_trans.py b/src/psyclone/psyir/transformations/parallel_loop_trans.py index de84d2985d..1f2bc3df35 100644 --- a/src/psyclone/psyir/transformations/parallel_loop_trans.py +++ b/src/psyclone/psyir/transformations/parallel_loop_trans.py @@ -318,7 +318,7 @@ def validate(self, node, options=None, **kwargs): f"The write-write dependency in '{var_name}'" f" cannot be solved by automatic array " f"privatisation. Use 'loop.explictly_private" - f"_sybmols.add(sybmol)' if *YOU* can guarantee" + f"_symbols.add(symbol)' if *YOU* can guarantee" f" that it is private.") continue # See if the scalar in question allows parallelisation of From 7f36e727d7825d1390ca7965cd36e1b4db3ed371 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 28 Nov 2025 15:53:03 +0000 Subject: [PATCH 49/50] #3150 tidy test docstring --- src/psyclone/tests/psyir/backend/sympy_writer_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psyclone/tests/psyir/backend/sympy_writer_test.py b/src/psyclone/tests/psyir/backend/sympy_writer_test.py index f57c606ce7..f9d718a8fc 100644 --- a/src/psyclone/tests/psyir/backend/sympy_writer_test.py +++ b/src/psyclone/tests/psyir/backend/sympy_writer_test.py @@ -675,7 +675,8 @@ def test_sym_writer_identical_variables_errors(): def test_sym_writer_intrinsiccall_node(fortran_reader): '''Handle edge cases for intrinsiccall node, i.e. when we have an - IntrinsicCall input that requires a call declaration (e.g. MVBITS).''' + IntrinsicCall input that requires an explicit 'call' (e.g. MVBITS). + ''' code = """subroutine test integer :: b call randomcall(b, b, b, b, b) From 41bbd0dfdd69328551990c9d5a67f615c6630ae3 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 28 Nov 2025 20:23:16 +0000 Subject: [PATCH 50/50] #3150 update changelog --- changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog b/changelog index 50ea88d0cc..0a21dfa039 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ + 5) PR #3150 towards #2302. Adds functionality to map arguments to + Fortran intrinsics to the corresponding named arguments. + 4) PR #3178 for #3196. Improves fparser2 reader directive handling. 3) PR #3210 for #3203. Removes outstanding references to the old,