def test_fusetrans_error_not_same_parent(): ''' Check that we reject attempts to fuse loops which don't share the same parent ''' from psyclone.transformations import LoopFuseTrans sch1 = Schedule() sch2 = Schedule() loop1 = Loop(variable=DataSymbol("i", INTEGER_TYPE), parent=sch1) loop2 = Loop(variable=DataSymbol("j", INTEGER_TYPE), parent=sch2) sch1.addchild(loop1) sch2.addchild(loop2) loop1.addchild(Literal("1", INTEGER_TYPE, parent=loop1)) # start loop1.addchild(Literal("10", INTEGER_TYPE, parent=loop1)) # stop loop1.addchild(Literal("1", INTEGER_TYPE, parent=loop1)) # step loop1.addchild(Schedule(parent=loop1)) # loop body loop2.addchild(Literal("1", INTEGER_TYPE, parent=loop2)) # start loop2.addchild(Literal("10", INTEGER_TYPE, parent=loop2)) # stop loop2.addchild(Literal("1", INTEGER_TYPE, parent=loop2)) # step loop2.addchild(Schedule(parent=loop2)) # loop body fuse = LoopFuseTrans() # Try to fuse loops with different parents with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. Loops do not have the " "same parent" in str(err.value))
def test_loop_fuse_trans(): ''' test of the loop-fuse transformation ''' _, info = parse(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "gunghoproto", "3_two_functions_shared_arguments.f90"), api="gunghoproto") psy = PSyFactory("gunghoproto").create(info) invoke = psy.invokes.get("invoke_0") schedule = invoke.schedule loop1 = schedule.children[0] loop2 = schedule.children[1] trans = LoopFuseTrans() schedule, _ = trans.apply(loop1, loop2) gen = str(psy.gen) for idx, line in enumerate(gen.split('\n')): if line.find("DO column=1,topology") != -1: do_idx = idx if line.find("CALL testkern1_code(") != -1: call1_idx = idx if line.find("CALL testkern2_code(") != -1: call2_idx = idx if line.find("END DO") != -1: enddo_idx = idx # 4 lines should be in sequence as calls have been fused into one loop assert enddo_idx-call2_idx == 1 and\ call2_idx-call1_idx == 1 and\ call1_idx-do_idx == 1
def test_script_trans(): ''' checks that generator.py works correctly when a transformation is provided as a script, i.e. it applies the transformations correctly. We use loop fusion as an example.''' from psyclone.parse import parse from psyclone.psyGen import PSyFactory from psyclone.transformations import LoopFuseTrans root_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.join(root_path, "test_files", "dynamo0p3") # first loop fuse explicitly (without using generator.py) parse_file = os.path.join(base_path, "4_multikernel_invokes.f90") _, invoke_info = parse(parse_file, api="dynamo0.3") psy = PSyFactory("dynamo0.3").create(invoke_info) invoke = psy.invokes.get("invoke_0") schedule = invoke.schedule loop1 = schedule.children[3] loop2 = schedule.children[4] trans = LoopFuseTrans() schedule.view() schedule, _ = trans.apply(loop1, loop2) invoke.schedule = schedule generated_code_1 = psy.gen schedule.view() # second loop fuse using generator.py and a script _, generated_code_2 = generate(parse_file, api="dynamo0.3", script_name=os.path.join( base_path, "loop_fuse_trans.py")) # remove module so we do not affect any following tests delete_module("loop_fuse_trans") # third - check that the results are the same ... assert str(generated_code_1) == str(generated_code_2)
def trans(psy): ''' a test loop fusion transformation for use with the transformation unit tests ''' from psyclone.transformations import LoopFuseTrans invoke = psy.invokes.get("invoke_0") schedule = invoke.schedule loop1 = schedule.children[3] loop2 = schedule.children[4] transform = LoopFuseTrans() schedule, _ = transform.apply(loop1, loop2) invoke.schedule = schedule return psy
def test_loop_fuse_non_adjacent_nodes(): ''' Test that an appropriate error is raised when we attempt to fuse two loops that are not adjacent to one another in the schedule ''' _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0") schedule = invoke.schedule lftrans = LoopFuseTrans() # Attempt to fuse two loops that are not adjacent to one another # in the schedule with pytest.raises(TransformationError): _, _ = lftrans.apply(schedule.children[0], schedule.children[2])
def test_loop_fuse_on_non_siblings(): ''' Test that an appropriate error is raised when we attempt to fuse two loops that do not share the same parent node in the schedule ''' _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0") schedule = invoke.schedule lftrans = LoopFuseTrans() # Attempt to fuse an outer loop with an inner loop with pytest.raises(TransformationError): _, _ = lftrans.apply(schedule.children[0], schedule.children[1]. children[0])
def test_loop_trans_name(): ''' Check that the name method works as expected. ''' # We have to use sub-classes of LoopTrans as it itself is abstract. trans1 = OMPParallelLoopTrans() assert trans1.name == "OMPParallelLoopTrans" trans2 = LoopFuseTrans() assert trans2.name == "LoopFuseTrans"
def test_loop_fuse_non_adjacent_nodes(): ''' Test that an appropriate error is raised when we attempt to fuse two loops that are not adjacent to one another in the schedule ''' _, info = parse(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "gocean0p1", "openmp_fuse_test.f90"), api=API) psy = PSyFactory(API).create(info) invoke = psy.invokes.get('invoke_0') schedule = invoke.schedule lftrans = LoopFuseTrans() # Attempt to fuse two loops that are not adjacent to one another # in the schedule with pytest.raises(TransformationError): _, _ = lftrans.apply(schedule.children[0], schedule.children[2])
def test_loop_fuse_different_iterates_over(): ''' Test that an appropriate error is raised when we attempt to fuse two loops that have differing values of ITERATES_OVER ''' _, invoke = get_invoke("test11_different_iterates_over_" "one_invoke.f90", 0) schedule = invoke.schedule lftrans = LoopFuseTrans() cbtrans = GOConstLoopBoundsTrans() # Attempt to fuse two loops that are iterating over different # things with pytest.raises(TransformationError): _, _ = lftrans.apply(schedule.children[0], schedule.children[1]) # Turn off constant loop bounds (which should have no effect) # and repeat newsched, _ = cbtrans.apply(schedule, const_bounds=False) with pytest.raises(TransformationError): _, _ = lftrans.apply(newsched.children[0], newsched.children[1])
def test_loop_fuse_with_not_a_loop(): ''' Test that an appropriate error is raised by the LoopFuseTrans base class wen we attempt to fuse a loop with something that is not a loop ''' _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0") schedule = invoke.schedule # Use the bare LoopFuseTrans in order tests its error checking lftrans = LoopFuseTrans() ompf = GOceanOMPParallelLoopTrans() # Enclose the first loop within an OMP parallel do new_sched, _ = ompf.apply(schedule.children[0]) # Attempt to (erroneously) fuse this OMP parallel do # with the next loop in the schedule with pytest.raises(TransformationError) as ex: schedule, _ = lftrans.apply(new_sched.children[0], new_sched.children[1]) # Exercise the __str__ method of TransformationError assert ("Target of LoopFuseTrans transformation must be a sub-class of " "Loop but got 'OMPParallelDoDirective'" in str(ex.value))
def test_loop_fuse_with_not_a_loop(): ''' Test that an appropriate error is raised by the LoopFuseTrans base class wen we attempt to fuse a loop with something that is not a loop ''' _, info = parse(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "gocean0p1", "openmp_fuse_test.f90"), api=API) psy = PSyFactory(API).create(info) invoke = psy.invokes.get('invoke_0') schedule = invoke.schedule # Use the bare LoopFuseTrans in order tests its error checking lftrans = LoopFuseTrans() ompf = GOceanOMPParallelLoopTrans() # Enclose the first loop within an OMP parallel do new_sched, _ = ompf.apply(schedule.children[0]) # Attempt to (erroneously) fuse this OMP parallel do # with the next loop in the schedule with pytest.raises(TransformationError) as ex: schedule, _ = lftrans.apply(new_sched.children[0], new_sched.children[1]) # Exercise the __str__ method of TransformationError assert "Transformation" in str(ex)
def test_fusetrans_error_incomplete(): ''' Check that we reject attempts to fuse loops which are incomplete. ''' from psyclone.psyir.nodes import Return from psyclone.transformations import LoopFuseTrans sch = Schedule() loop1 = Loop(variable=DataSymbol("i", INTEGER_TYPE), parent=sch) loop2 = Loop(variable=DataSymbol("j", INTEGER_TYPE), parent=sch) sch.addchild(loop1) sch.addchild(loop2) fuse = LoopFuseTrans() # Check first loop with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. The target loop must have " "four children but found: []" in str(err.value)) loop1.addchild(Literal("start", INTEGER_TYPE, parent=loop1)) loop1.addchild(Literal("stop", INTEGER_TYPE, parent=loop1)) loop1.addchild(Literal("step", INTEGER_TYPE, parent=loop1)) loop1.addchild(Schedule(parent=loop1)) loop1.loop_body.addchild(Return(parent=loop1.loop_body)) # Check second loop with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. The target loop must have " "four children but found: []" in str(err.value)) loop2.addchild(Literal("start", INTEGER_TYPE, parent=loop2)) loop2.addchild(Literal("stop", INTEGER_TYPE, parent=loop2)) loop2.addchild(Literal("step", INTEGER_TYPE, parent=loop2)) loop2.addchild(Schedule(parent=loop2)) loop2.loop_body.addchild(Return(parent=loop2.loop_body)) # Validation should now pass fuse.validate(loop1, loop2)
def test_fusetrans_error_incomplete(): ''' Check that we reject attempts to fuse loops which are incomplete. ''' from psyclone.psyGen import Loop, Schedule, Literal, Return from psyclone.transformations import LoopFuseTrans, TransformationError sch = Schedule() loop1 = Loop(variable_name="i", parent=sch) loop2 = Loop(variable_name="j", parent=sch) sch.addchild(loop1) sch.addchild(loop2) fuse = LoopFuseTrans() # Check first loop with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert "Error in LoopFuse transformation. The first loop does not have " \ "4 children." in str(err.value) loop1.addchild(Literal("start", parent=loop1)) loop1.addchild(Literal("stop", parent=loop1)) loop1.addchild(Literal("step", parent=loop1)) loop1.addchild(Schedule(parent=loop1)) loop1.loop_body.addchild(Return(parent=loop1.loop_body)) # Check second loop with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert "Error in LoopFuse transformation. The second loop does not have " \ "4 children." in str(err.value) loop2.addchild(Literal("start", parent=loop2)) loop2.addchild(Literal("stop", parent=loop2)) loop2.addchild(Literal("step", parent=loop2)) loop2.addchild(Schedule(parent=loop2)) loop2.loop_body.addchild(Return(parent=loop2.loop_body)) # Validation should now pass fuse.validate(loop1, loop2)