def test_ifblock_children_region(): ''' Check that we reject attempts to transform the conditional part of an If statement or to include both the if- and else-clauses in a region (without their parent). ''' from psyclone.psyGen import IfBlock, Reference, Schedule from psyclone.transformations import ACCParallelTrans, TransformationError acct = ACCParallelTrans() # Construct a valid IfBlock ifblock = IfBlock() # Condition ref1 = Reference('condition1', parent=ifblock) ifblock.addchild(ref1) # If-body sch = Schedule(parent=ifblock) ifblock.addchild(sch) # Else-body sch2 = Schedule(parent=ifblock) ifblock.addchild(sch2) # Attempt to put all of the children of the IfBlock into a region. This # is an error because the first child is the conditional part of the # IfBlock. with pytest.raises(TransformationError) as err: super(ACCParallelTrans, acct)._validate(ifblock.children) assert ("transformation to the conditional expression (first child" in str(err)) with pytest.raises(TransformationError) as err: super(ACCParallelTrans, acct)._validate(ifblock.children[1:]) assert ("Cannot enclose both the if- and else- clauses of an IfBlock by " in str(err))
def trans(psy): ''' Take the supplied psy object, apply OpenACC transformations to the schedule of the first invoke and return the new psy object ''' from psyclone.transformations import ACCParallelTrans, \ ACCEnterDataTrans, ACCLoopTrans, ACCRoutineTrans, \ KernelGlobalsToArguments ptrans = ACCParallelTrans() ltrans = ACCLoopTrans() dtrans = ACCEnterDataTrans() ktrans = ACCRoutineTrans() g2localtrans = KernelGlobalsToArguments() invoke = psy.invokes.invoke_list[0] schedule = invoke.schedule schedule.view() # Apply the OpenACC Loop transformation to *every* loop # nest in the schedule from psyclone.psyir.nodes import Loop for child in schedule.children: if isinstance(child, Loop): newschedule, _ = ltrans.apply(child, {"collapse": 2}) schedule = newschedule # Put all of the loops in a single parallel region newschedule, _ = ptrans.apply(schedule.children) # Add an enter-data directive newschedule, _ = dtrans.apply(schedule) # Convert any accesses to global data into kernel arguments and then # put an 'acc routine' directive inside each kernel for kern in schedule.coded_kernels(): if kern.name == "kern_use_var_code": # TODO #490 and #663. This currently won't work because the # KernelGlobalsToArguments transformation works on the PSyIR but # the subsequent ACCRoutineTrans works on the fparser2 parse tree. g2localtrans.apply(kern) _, _ = ktrans.apply(kern) # Ideally we would module-inline the kernel here (to save having to # rely on the compiler to do it) but this does not currently work # for the fparser2 AST (issue #229). # _, _ = itrans.apply(kern) invoke.schedule = newschedule newschedule.view() return psy
def trans(psy): ''' Take the supplied psy object, apply OpenACC transformations to the schedule of the first invoke and return the new psy object ''' from psyclone.transformations import ACCParallelTrans, \ ACCEnterDataTrans, ACCLoopTrans, ACCRoutineTrans, \ KernelGlobalsToArguments, KernelModuleInlineTrans ptrans = ACCParallelTrans() ltrans = ACCLoopTrans() dtrans = ACCEnterDataTrans() ktrans = ACCRoutineTrans() itrans = KernelModuleInlineTrans() g2localtrans = KernelGlobalsToArguments() invoke = psy.invokes.invoke_list[0] schedule = invoke.schedule schedule.view() # Apply the OpenACC Loop transformation to *every* loop # nest in the schedule from psyclone.psyir.nodes import Loop for child in schedule.children: if isinstance(child, Loop): newschedule, _ = ltrans.apply(child, {"collapse": 2}) schedule = newschedule # Put all of the loops in a single parallel region newschedule, _ = ptrans.apply(schedule.children) # Add an enter-data directive newschedule, _ = dtrans.apply(schedule) # Convert any accesses to global data into kernel arguments, put an # 'acc routine' directive inside, and module-inline each kernel for kern in schedule.coded_kernels(): if kern.name == "kern_use_var_code": g2localtrans.apply(kern) _, _ = ktrans.apply(kern) _, _ = itrans.apply(kern) invoke.schedule = newschedule newschedule.view() return psy
def trans(psy): '''PSyclone transformation script for the dynamo0p3 api to apply OpenACC loop, parallel and enter data directives generically. ''' loop_trans = ACCLoopTrans() parallel_trans = ACCParallelTrans() enter_data_trans = ACCEnterDataTrans() # Loop over all of the Invokes in the PSy object for invoke in psy.invokes.invoke_list: print("Transforming invoke '" + invoke.name + "'...") schedule = invoke.schedule for loop in schedule.loops(): _, _ = loop_trans.apply(loop) _, _ = parallel_trans.apply(loop.parent) _, _ = enter_data_trans.apply(schedule) return psy
def test_regiontrans_wrong_options(): '''Check that the validate method raises the expected error if passed options that are not contained in a dictionary. ''' # RegionTrans is abstract so use a concrete sub-class region_trans = ACCParallelTrans() with pytest.raises(TransformationError) as excinfo: RegionTrans.validate(region_trans, None, options="invalid") assert ("Transformation apply method options argument must be a " "dictionary but found 'str'." in str(excinfo.value))
def trans(psy): ''' Take the supplied psy object, apply OpenACC transformations to the schedule of invoke_0 and return the new psy object ''' from psyclone.transformations import ACCParallelTrans, \ ACCDataTrans, ACCLoopTrans, ACCRoutineTrans, KernelModuleInlineTrans ptrans = ACCParallelTrans() ltrans = ACCLoopTrans() dtrans = ACCDataTrans() ktrans = ACCRoutineTrans() itrans = KernelModuleInlineTrans() invoke = psy.invokes.get('invoke_0_inc_field') schedule = invoke.schedule # schedule.view() # Apply the OpenACC Loop transformation to *every* loop # nest in the schedule from psyclone.psyGen import Loop for child in schedule.children: if isinstance(child, Loop): newschedule, _ = ltrans.apply(child, collapse=2) schedule = newschedule # Put all of the loops in a single parallel region newschedule, _ = ptrans.apply(schedule.children) # Add an enter-data directive newschedule, _ = dtrans.apply(schedule) # Put an 'acc routine' directive inside each kernel for kern in schedule.kern_calls(): _, _ = ktrans.apply(kern) # Ideally we would module-inline the kernel here (to save having to # rely on the compiler to do it) but this does not currently work # for the fparser2 AST (issue #229). # _, _ = itrans.apply(kern) invoke.schedule = newschedule newschedule.view() return psy
def test_lfric_stencil(): '''Check variable usage detection when OpenACC is used with a kernel that uses a stencil. ''' # Use the OpenACC transforms to create the required kernels acc_par_trans = ACCParallelTrans() acc_enter_trans = ACCEnterDataTrans() _, invoke = get_invoke("14.4_halo_vector.f90", "dynamo0.3", idx=0, dist_mem=False) sched = invoke.schedule _ = acc_par_trans.apply(sched.children) _ = acc_enter_trans.apply(sched) # Find the first kernel: kern = invoke.schedule.walk(CodedKern)[0] create_acc_arg_list = KernCallAccArgList(kern) var_accesses = VariablesAccessInfo() create_acc_arg_list.generate(var_accesses=var_accesses) var_info = str(var_accesses) assert "f1: READ+WRITE" in var_info assert "f2: READ" in var_info assert "f2_stencil_dofmap: READ" in var_info
def test_lfric_acc_operator(): '''Check variable usage detection when OpenACC is used with a kernel that uses an operator. ''' # Use the OpenACC transforms to enclose the kernels # with OpenACC directives. acc_par_trans = ACCParallelTrans() acc_enter_trans = ACCEnterDataTrans() _, invoke = get_invoke("20.0_cma_assembly.f90", "dynamo0.3", idx=0, dist_mem=False) sched = invoke.schedule _ = acc_par_trans.apply(sched.children) _ = acc_enter_trans.apply(sched) # Find the first kernel: kern = invoke.schedule.walk(CodedKern)[0] create_acc_arg_list = KernCallAccArgList(kern) var_accesses = VariablesAccessInfo() create_acc_arg_list.generate(var_accesses=var_accesses) var_info = str(var_accesses) assert "lma_op1_proxy%ncell_3d: READ" in var_info assert "lma_op1_proxy%local_stencil: WRITE" in var_info
def test_regiontrans_wrong_children(): ''' Check that the validate method raises the expected error if passed the wrong children of a Node. (e.g. those representing the bounds of a Loop.) ''' # RegionTrans is abstract so use a concrete sub-class rtrans = ACCParallelTrans() # Construct a valid Loop in the PSyIR parent = Loop(parent=None) parent.addchild(Literal("1", INTEGER_TYPE, parent)) parent.addchild(Literal("10", INTEGER_TYPE, parent)) parent.addchild(Literal("1", INTEGER_TYPE, parent)) parent.addchild(Schedule(parent=parent)) with pytest.raises(TransformationError) as err: RegionTrans.validate(rtrans, parent.children) assert ("Cannot apply a transformation to multiple nodes when one or more " "is a Schedule" in str(err.value))
def test_ifblock_children_region(): ''' Check that we reject attempts to transform the conditional part of an If statement or to include both the if- and else-clauses in a region (without their parent). ''' acct = ACCParallelTrans() # Construct a valid IfBlock condition = Reference(DataSymbol('condition', BOOLEAN_TYPE)) ifblock = IfBlock.create(condition, [], []) # Attempt to put all of the children of the IfBlock into a region. This # is an error because the first child is the conditional part of the # IfBlock. with pytest.raises(TransformationError) as err: super(ACCParallelTrans, acct).validate([ifblock.children[0]]) assert ("transformation to the immediate children of a Loop/IfBlock " "unless it is to a single Schedule" in str(err.value)) with pytest.raises(TransformationError) as err: super(ACCParallelTrans, acct).validate(ifblock.children[1:]) assert (" to multiple nodes when one or more is a Schedule. " "Either target a single Schedule or " in str(err.value))
def test_accparallel(): ''' Generic tests for the ACCParallelTrans class ''' acct = ACCParallelTrans() assert acct.name == "ACCParallelTrans"
def test_accparallel(): ''' Generic tests for the ACCParallelTrans class ''' from psyclone.transformations import ACCParallelTrans acct = ACCParallelTrans() assert acct.name == "ACCParallelTrans"