Exemplo n.º 1
0
def test_correct(func, output, tmpdir):
    '''Check that a valid example produces the expected output when the
    argument to ABS is a simple argument and when it is an
    expresssion.

    '''
    Config.get().api = "nemo"
    operation = example_psyir(func)
    writer = FortranWriter()
    result = writer(operation.root)
    assert ("subroutine abs_example(arg)\n"
            "  real, intent(inout) :: arg\n"
            "  real :: psyir_tmp\n\n"
            "  psyir_tmp = ABS({0})\n\n"
            "end subroutine abs_example\n".format(output)) in result
    trans = Abs2CodeTrans()
    trans.apply(operation, operation.root.symbol_table)
    result = writer(operation.root)
    assert ("subroutine abs_example(arg)\n"
            "  real, intent(inout) :: arg\n"
            "  real :: psyir_tmp\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"
            "  psyir_tmp = res_abs\n\n"
            "end subroutine abs_example\n".format(output)) in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
Exemplo n.º 2
0
def test_invalid():
    '''Check that the validate tests are run when the apply method is
    called.'''
    trans = Abs2CodeTrans()
    with pytest.raises(TransformationError) as excinfo:
        trans.apply(None)
    assert (
        "Error in Abs2CodeTrans transformation. The supplied node argument "
        "is not a ABS operator, found 'NoneType'." in str(excinfo.value))
Exemplo n.º 3
0
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
Exemplo n.º 4
0
def test_correct_2abs(tmpdir):
    '''Check that a valid example produces the expected output when there
    is more than one ABS() in 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
    abs_op = UnaryOperation.create(UnaryOperation.Operator.ABS,
                                   Literal("1.0", REAL_TYPE))
    operation.detach()
    op1 = BinaryOperation.create(BinaryOperation.Operator.ADD,
                                 operation, abs_op)
    assignment.addchild(op1)
    writer = FortranWriter()
    result = writer(root)
    assert (
        "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"
        "end subroutine abs_example\n") in result
    trans = Abs2CodeTrans()
    trans.apply(operation, root.symbol_table)
    trans.apply(abs_op, root.symbol_table)
    result = writer(root)
    assert (
        "subroutine abs_example(arg)\n"
        "  real, intent(inout) :: arg\n"
        "  real :: psyir_tmp\n"
        "  real :: res_abs\n"
        "  real :: tmp_abs\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"
        "  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"
        "  psyir_tmp = res_abs + res_abs_1\n\n"
        "end subroutine abs_example\n") in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
Exemplo n.º 5
0
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 = Abs2CodeTrans()
    assert trans._operator_name == "ABS"
    assert trans._classes == (UnaryOperation, )
    assert trans._operators == (UnaryOperation.Operator.ABS, )
    assert (str(trans) == "Convert the PSyIR ABS intrinsic to equivalent "
            "PSyIR code.")
    assert trans.name == "Abs2CodeTrans"
Exemplo n.º 6
0
def test_correct_expr(tmpdir):
    '''Check that a valid example produces the expected output when ABS()
    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)))
    assignment = operation.parent
    op1 = BinaryOperation.create(BinaryOperation.Operator.ADD,
                                 Literal("1.0", REAL_TYPE), operation)
    op2 = BinaryOperation.create(BinaryOperation.Operator.ADD, op1,
                                 Literal("2.0", REAL_TYPE))
    op2.parent = assignment
    assignment.children[1] = op2
    writer = FortranWriter()
    result = writer(operation.root)
    assert ("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"
            "end subroutine abs_example\n") in result
    trans = Abs2CodeTrans()
    trans.apply(operation, operation.root.symbol_table)
    result = writer(operation.root)
    assert ("subroutine abs_example(arg)\n"
            "  real, intent(inout) :: arg\n"
            "  real :: psyir_tmp\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"
            "  psyir_tmp = 1.0 + res_abs + 2.0\n\n"
            "end subroutine abs_example\n") in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
Exemplo n.º 7
0
    def apply(self, node, options=None):
        '''Apply the SIGN intrinsic conversion transformation to the specified
        node. This node must be a SIGN BinaryOperation. The SIGN
        BinaryOperation is converted to equivalent inline code. This
        is implemented as a PSyIR transform from:

        .. code-block:: python

            R = ... SIGN(A, B) ...

        to:

        .. code-block:: python

            tmp_abs = A
            if tmp_abs < 0.0:
                res_abs = tmp_abs*-1.0
            else:
                res_abs = tmp_abs
            res_sign = res_abs
            tmp_sign = B
            if tmp_sign < 0.0:
                res_sign = res_sign*-1.0
            R = ... res_sign ...

        where ``A`` and ``B`` could be arbitrarily complex PSyIR
        expressions, ``...`` could be arbitrary PSyIR code and where
        ``ABS`` has been replaced with inline code by the NemoAbsTrans
        transformation.

        This transformation requires the operation node to be a
        descendent of an assignment and will raise an exception if
        this is not the case.

        :param node: a SIGN BinaryOperation node.
        :type node: :py:class:`psyclone.psyGen.BinaryOperation`
        :param symbol_table: the symbol table.
        :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
        :param options: a dictionary with options for transformations.
        :type options: dictionary of string:values or None

        '''
        # pylint: disable=too-many-locals
        self.validate(node)

        schedule = node.root
        symbol_table = schedule.symbol_table

        oper_parent = node.parent
        assignment = node.ancestor(Assignment)
        # Create two temporary variables.  There is an assumption here
        # that the SIGN Operator returns a PSyIR real type. This might
        # not be what is wanted (e.g. the args might PSyIR integers),
        # or there may be errors (arguments are of different types)
        # but this can't be checked as we don't have the appropriate
        # methods to query nodes (see #658).
        res_var_symbol = symbol_table.new_symbol("res_sign",
                                                 symbol_type=DataSymbol,
                                                 datatype=REAL_TYPE)
        tmp_var_symbol = symbol_table.new_symbol("tmp_sign",
                                                 symbol_type=DataSymbol,
                                                 datatype=REAL_TYPE)

        # Replace operator with a temporary (res_var).
        oper_parent.children[node.position] = Reference(res_var_symbol,
                                                        parent=oper_parent)

        # Extract the operand nodes
        op1, op2 = node.pop_all_children()

        # res_var=ABS(A)
        lhs = Reference(res_var_symbol)
        rhs = UnaryOperation.create(UnaryOperation.Operator.ABS, op1)
        new_assignment = Assignment.create(lhs, rhs)
        assignment.parent.children.insert(assignment.position, new_assignment)

        # Replace the ABS intrinsic with inline code.
        abs_trans = Abs2CodeTrans()
        abs_trans.apply(rhs, symbol_table)

        # tmp_var=B
        lhs = Reference(tmp_var_symbol)
        new_assignment = Assignment.create(lhs, op2)
        assignment.parent.children.insert(assignment.position, new_assignment)

        # if_condition: tmp_var<0.0
        lhs = Reference(tmp_var_symbol)
        rhs = Literal("0.0", REAL_TYPE)
        if_condition = BinaryOperation.create(BinaryOperation.Operator.LT, lhs,
                                              rhs)

        # then_body: res_var=res_var*-1.0
        lhs = Reference(res_var_symbol)
        lhs_child = Reference(res_var_symbol)
        rhs_child = Literal("-1.0", REAL_TYPE)
        rhs = BinaryOperation.create(BinaryOperation.Operator.MUL, lhs_child,
                                     rhs_child)
        then_body = [Assignment.create(lhs, rhs)]

        # if [if_condition] then [then_body]
        if_stmt = IfBlock.create(if_condition, then_body)
        assignment.parent.children.insert(assignment.position, if_stmt)
Exemplo n.º 8
0
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)