def test_invalid(): '''Check that the validate tests are run when the apply method is called.''' trans = Sign2CodeTrans() with pytest.raises(TransformationError) as excinfo: trans.apply(None) assert ( "Error in Sign2CodeTrans transformation. The supplied node argument " "is not a SIGN operator, found 'NoneType'." in str(excinfo.value))
def trans(psy): '''Transformation routine for use with PSyclone. Applies the PSyIR2SIR transform to the supplied invokes after replacing any ABS, SIGN or MIN intrinsics with equivalent code. This is done because the SIR does not support intrinsics. :param psy: the PSy object which this script will transform. :type psy: :py:class:`psyclone.psyGen.PSy` :returns: the transformed PSy object. :rtype: :py:class:`psyclone.psyGen.PSy` ''' abs_trans = Abs2CodeTrans() sign_trans = Sign2CodeTrans() min_trans = Min2CodeTrans() nemo_loop_trans = NemoAllArrayRange2LoopTrans() sir_writer = SIRWriter() fortran_writer = FortranWriter() # For each Invoke write out the SIR representation of the # schedule. Note, there is no algorithm layer in the NEMO API so # the invokes represent all of the original code. for invoke in psy.invokes.invoke_list: schedule = invoke.schedule for assignment in schedule.walk(Assignment): nemo_loop_trans.apply(assignment) for kernel in schedule.walk(NemoKern): # The NEMO api currently has no symbol table so create one # to allow the generation of new variables. Note, this # does not guarantee unique names as we don't know any of # the existing names (so generated names could clash). symbol_table = SymbolTable() kernel_schedule = kernel.get_kernel_schedule() for oper in kernel_schedule.walk(Operation): if oper.operator == UnaryOperation.Operator.ABS: # Apply ABS transformation abs_trans.apply(oper, symbol_table) elif oper.operator == BinaryOperation.Operator.SIGN: # Apply SIGN transformation sign_trans.apply(oper, symbol_table) elif oper.operator in [ BinaryOperation.Operator.MIN, NaryOperation.Operator.MIN ]: # Apply (2-n arg) MIN transformation min_trans.apply(oper, symbol_table) kern = fortran_writer(schedule) print(kern) kern = sir_writer(schedule) print(kern) return psy
def test_initialise(): '''Check that variables are set up as expected when an instance of the class is created and that the str and name methods work as expected. ''' trans = Sign2CodeTrans() assert trans._operator_name == "SIGN" assert trans._classes == (BinaryOperation, ) assert trans._operators == (BinaryOperation.Operator.SIGN, ) assert (str(trans) == "Convert the PSyIR SIGN intrinsic to equivalent " "PSyIR code.") assert trans.name == "Sign2CodeTrans"
def test_correct_expr(tmpdir): '''Check that a valid example produces the expected output when SIGN is part of an expression. ''' Config.get().api = "nemo" operation = example_psyir(lambda arg: BinaryOperation.create( BinaryOperation.Operator.MUL, arg, Literal("3.14", REAL_TYPE))) root = operation.root assignment = operation.parent operation.detach() op1 = BinaryOperation.create(BinaryOperation.Operator.ADD, Literal("1.0", REAL_TYPE), operation) op2 = BinaryOperation.create(BinaryOperation.Operator.ADD, op1, Literal("2.0", REAL_TYPE)) assignment.addchild(op2) writer = FortranWriter() result = writer(root) assert ("subroutine sign_example(arg, arg_1)\n" " 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" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(operation, root.symbol_table) result = writer(root) assert ("subroutine sign_example(arg, arg_1)\n" " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n" " real :: res_sign\n" " real :: tmp_sign\n" " real :: res_abs\n" " real :: tmp_abs\n\n" " tmp_abs = arg * 3.14\n" " if (tmp_abs > 0.0) then\n" " res_abs = tmp_abs\n" " else\n" " res_abs = tmp_abs * -1.0\n" " end if\n" " res_sign = res_abs\n" " tmp_sign = arg_1\n" " if (tmp_sign < 0.0) then\n" " res_sign = res_sign * -1.0\n" " end if\n" " psyir_tmp = 1.0 + res_sign + 2.0\n\n" "end subroutine sign_example\n") in result assert Compile(tmpdir).string_compiles(result) # Remove the created config instance Config._instance = None
def test_correct(func, output, tmpdir): '''Check that a valid example produces the expected output when the first argument to SIGN is a simple argument and when it is an expression. ''' Config.get().api = "nemo" operation = example_psyir(func) root = operation.root writer = FortranWriter() result = writer(root) assert ("subroutine sign_example(arg, arg_1)\n" " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n\n" " psyir_tmp = SIGN({0}, arg_1)\n\n" "end subroutine sign_example\n".format(output)) in result trans = Sign2CodeTrans() trans.apply(operation, root.symbol_table) result = writer(root) assert ("subroutine sign_example(arg, arg_1)\n" " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n" " real :: res_sign\n" " real :: tmp_sign\n" " real :: res_abs\n" " real :: tmp_abs\n\n" " tmp_abs = {0}\n" " if (tmp_abs > 0.0) then\n" " res_abs = tmp_abs\n" " else\n" " res_abs = tmp_abs * -1.0\n" " end if\n" " res_sign = res_abs\n" " tmp_sign = arg_1\n" " if (tmp_sign < 0.0) then\n" " res_sign = res_sign * -1.0\n" " end if\n" " psyir_tmp = res_sign\n\n" "end subroutine sign_example\n".format(output)) in result assert Compile(tmpdir).string_compiles(result) # Remove the created config instance Config._instance = None
def test_correct_2sign(tmpdir): '''Check that a valid example produces the expected output when there is more than one SIGN in an expression. ''' Config.get().api = "nemo" operation = example_psyir(lambda arg: BinaryOperation.create( BinaryOperation.Operator.MUL, arg, Literal("3.14", REAL_TYPE))) assignment = operation.parent sign_op = BinaryOperation.create(BinaryOperation.Operator.SIGN, Literal("1.0", REAL_TYPE), Literal("1.0", REAL_TYPE)) op1 = BinaryOperation.create(BinaryOperation.Operator.ADD, sign_op, operation) op1.parent = assignment assignment.children[1] = op1 writer = FortranWriter() result = writer(operation.root) assert ("subroutine sign_example(arg,arg_1)\n" " 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" "end subroutine sign_example\n") in result trans = Sign2CodeTrans() trans.apply(operation, operation.root.symbol_table) trans.apply(sign_op, operation.root.symbol_table) result = writer(operation.root) assert ("subroutine sign_example(arg,arg_1)\n" " real, intent(inout) :: arg\n" " real, intent(inout) :: arg_1\n" " real :: psyir_tmp\n" " real :: res_sign\n" " real :: tmp_sign\n" " real :: res_abs\n" " real :: tmp_abs\n" " real :: res_sign_1\n" " real :: tmp_sign_1\n" " real :: res_abs_1\n" " real :: tmp_abs_1\n\n" " tmp_abs=arg * 3.14\n" " if (tmp_abs > 0.0) then\n" " res_abs=tmp_abs\n" " else\n" " res_abs=tmp_abs * -1.0\n" " end if\n" " res_sign=res_abs\n" " tmp_sign=arg_1\n" " if (tmp_sign < 0.0) then\n" " res_sign=res_sign * -1.0\n" " end if\n" " tmp_abs_1=1.0\n" " if (tmp_abs_1 > 0.0) then\n" " res_abs_1=tmp_abs_1\n" " else\n" " res_abs_1=tmp_abs_1 * -1.0\n" " end if\n" " res_sign_1=res_abs_1\n" " tmp_sign_1=1.0\n" " if (tmp_sign_1 < 0.0) then\n" " res_sign_1=res_sign_1 * -1.0\n" " end if\n" " psyir_tmp=res_sign_1 + res_sign\n\n" "end subroutine sign_example\n") in result assert Compile(tmpdir).string_compiles(result) # Remove the created config instance Config._instance = None
def make_sir_compliant(schedule): ''' Applies various transformations to the supplied schedule to replace any features that cannot be represented in SIR with alternative forms: 1. Converts any accesses of individual array elements into 1-trip loops. 2. Transforms array assignments into loops. 3. Replaces any ABS, SIGN, MIN or MAX intrinsics with equivalent PSyIR. 4. Hoists any loop-invariant assignments out of loops over levels. :param schedule: the schedule to transform. :type schedule: :py:class:`psyclone.psyir.nodes.Schedule` ''' abs_trans = Abs2CodeTrans() sign_trans = Sign2CodeTrans() min_trans = Min2CodeTrans() max_trans = Max2CodeTrans() array_range_trans = NemoAllArrayRange2LoopTrans() array_access_trans = NemoAllArrayAccess2LoopTrans() hoist_trans = HoistTrans() # Transform any single index accesses in array assignments # (e.g. a(1)) into 1-trip loops. for assignment in schedule.walk(Assignment): array_access_trans.apply(assignment) # Transform any array assignments (Fortran ':' notation) into loops. for assignment in schedule.walk(Assignment): array_range_trans.apply(assignment) for kernel in schedule.walk(NemoKern): kernel_schedule = kernel.get_kernel_schedule() for oper in kernel_schedule.walk(Operation): if oper.operator == UnaryOperation.Operator.ABS: # Apply ABS transformation abs_trans.apply(oper) elif oper.operator == BinaryOperation.Operator.SIGN: # Apply SIGN transformation sign_trans.apply(oper) elif oper.operator in [BinaryOperation.Operator.MIN, NaryOperation.Operator.MIN]: # Apply (2-n arg) MIN transformation min_trans.apply(oper) elif oper.operator in [BinaryOperation.Operator.MAX, NaryOperation.Operator.MAX]: # Apply (2-n arg) MAX transformation max_trans.apply(oper) # Remove any loop invariant assignments inside k-loops to make # them perfectly nested. At the moment this transformation # does not perform any dependence analysis validation so could # move code that should not be moved, see issue # #1387. However, it is known that it is safe do apply this # transformation to this particular code # (tra_adv_compute.F90). for loop in schedule.loops(): # outermost only if loop.loop_type == "levels": for child in loop.loop_body[:]: if isinstance(child, Assignment): hoist_trans.apply(child)