Beispiel #1
0
def create_expr(symbol_table):
    '''Utility routine that creates and returns an expression containing a
    number of array references containing range nodes. The expression
    looks like the following (using Fortran array notation):

    x(2:n:2)*z(1,2:n:2)+a(1)

    :param symbol_table: the symbol table to which we add the array \
        symbols.
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an expression containing 3 array references, 2 of which \
        contain ranges.
    :rtype: :py:class:`psyclone.psyir.nodes.BinaryOperation`

    '''
    array_symbol = DataSymbol("x", ArrayType(REAL_TYPE, [10]))
    symbol_table.add(array_symbol)
    symbol_n = symbol_table.lookup("n")
    array_x = Array.create(array_symbol, [create_stepped_range(symbol_n)])
    array_symbol = DataSymbol("z", ArrayType(REAL_TYPE, [10, 10]))
    symbol_table.add(array_symbol)
    array_z = Array.create(
        array_symbol,
        [Literal("1", INTEGER_TYPE),
         create_stepped_range(symbol_n)])
    array_symbol = DataSymbol("a", ArrayType(REAL_TYPE, [10]))
    array_a = Array.create(array_symbol, [Literal("1", INTEGER_TYPE)])
    symbol_table.add(array_symbol)
    mult = BinaryOperation.create(BinaryOperation.Operator.MUL, array_x,
                                  array_z)
    add = BinaryOperation.create(BinaryOperation.Operator.ADD, mult, array_a)
    return add
Beispiel #2
0
def test_array_range_with_reduction():
    ''' Test that we correctly identify an array range when it is the result
        of a reduction from an array, e.g x(1, INT(SUM(map(:, :), 1))) = 1.0

    '''
    one = Literal("1.0", REAL_TYPE)
    int_one = Literal("1", INTEGER_TYPE)
    int_two = Literal("2", INTEGER_TYPE)
    int_array_type = ArrayType(INTEGER_SINGLE_TYPE, [10, 10])
    map_sym = DataSymbol("map", int_array_type)
    array_type = ArrayType(REAL_TYPE, [10, 10])
    symbol = DataSymbol("x", array_type)
    lbound1 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(map_sym), int_one.copy())
    ubound1 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(map_sym), int_one.copy())
    my_range1 = Range.create(lbound1, ubound1)
    lbound2 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(map_sym), int_two.copy())
    ubound2 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(map_sym), int_two.copy())
    my_range2 = Range.create(lbound2, ubound2)
    bsum_op = BinaryOperation.create(
        BinaryOperation.Operator.SUM,
        ArrayReference.create(map_sym, [my_range1, my_range2]), int_one.copy())
    int_op2 = UnaryOperation.create(UnaryOperation.Operator.INT, bsum_op)
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), int_op2]), one.copy())
    assert assignment.is_array_range is True
Beispiel #3
0
def test_binaryoperation_create_invalid():
    '''Test that the create method in a BinaryOperation class raises the
    expected exception if the provided input is invalid.

    '''
    ref1 = Reference(DataSymbol("tmp1", REAL_SINGLE_TYPE))
    ref2 = Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE))
    add = BinaryOperation.Operator.ADD

    # oper not a BinaryOperation.Operator.
    with pytest.raises(GenerationError) as excinfo:
        _ = BinaryOperation.create("invalid", ref1, ref2)
    assert ("oper argument in create method of BinaryOperation class should "
            "be a PSyIR BinaryOperation Operator but found 'str'."
            in str(excinfo.value))

    # lhs not a Node.
    with pytest.raises(GenerationError) as excinfo:
        _ = BinaryOperation.create(add, "invalid", ref2)
    assert ("Item 'str' can't be child 0 of 'BinaryOperation'. The valid "
            "format is: 'DataNode, DataNode'.") in str(excinfo.value)

    # rhs not a Node.
    with pytest.raises(GenerationError) as excinfo:
        _ = BinaryOperation.create(add, ref1, "invalid")
    assert ("Item 'str' can't be child 1 of 'BinaryOperation'. The valid "
            "format is: 'DataNode, DataNode'.") in str(excinfo.value)
Beispiel #4
0
def test_range_references_props():
    ''' Test that the properties of a Range return what we expect
    when the start, stop and step are references or expressions. '''
    from psyclone.psyir.nodes import BinaryOperation, KernelSchedule
    sched = KernelSchedule("test_sched")
    sym_table = sched.symbol_table
    start_symbol = DataSymbol("istart", INTEGER_SINGLE_TYPE)
    stop_symbol = DataSymbol("istop", INTEGER_SINGLE_TYPE)
    step_symbol = DataSymbol("istep", INTEGER_SINGLE_TYPE)
    sym_table.add(start_symbol)
    sym_table.add(stop_symbol)
    sym_table.add(step_symbol)
    startvar = Reference(start_symbol)
    stopvar = Reference(stop_symbol)
    start = BinaryOperation.create(BinaryOperation.Operator.SUB, startvar,
                                   Literal("1", INTEGER_SINGLE_TYPE))
    stop = BinaryOperation.create(BinaryOperation.Operator.ADD, stopvar,
                                  Literal("1", INTEGER_SINGLE_TYPE))
    step = Reference(step_symbol)
    erange = Range.create(start, stop, step)
    assert erange.start is start
    assert erange.stop is stop
    assert erange.step is step
    assert erange.children[0] is start
    assert erange.children[1] is stop
    assert erange.children[2] is step
def test_type_convert_binaryop_create(operation, op_str):
    '''Test that the create method in the BinaryOperation class correctly
    creates a BinaryOperation instance for the REAL and INT type-conversion
    operations..

    '''
    sym = DataSymbol("tmp1", REAL_SINGLE_TYPE)
    lhs = Reference(sym)
    wp_sym = DataSymbol("wp", INTEGER_SINGLE_TYPE)
    # Reference to a kind parameter
    rhs = Reference(wp_sym)
    binaryoperation = BinaryOperation.create(operation, lhs, rhs)
    assert binaryoperation._operator is operation
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert op_str + "(tmp1, wp)" in result.lower()
    # Kind specified with an integer literal
    rhs = Literal("4", INTEGER_SINGLE_TYPE)
    binaryoperation = BinaryOperation.create(operation, lhs.detach(), rhs)
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert op_str + "(tmp1, 4)" in result.lower()
    # Kind specified as an arithmetic expression
    rhs = BinaryOperation.create(BinaryOperation.Operator.ADD,
                                 Reference(wp_sym),
                                 Literal("2", INTEGER_SINGLE_TYPE))
    binaryoperation = BinaryOperation.create(operation, lhs.detach(), rhs)
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert op_str + "(tmp1, wp + 2)" in result.lower()
Beispiel #6
0
def test_is_not_array_range():
    ''' Test that is_array_range correctly rejects things that aren't
    an assignment to an array range.

    '''
    int_one = Literal("1", INTEGER_SINGLE_TYPE)
    one = Literal("1.0", REAL_TYPE)
    var = DataSymbol("x", REAL_TYPE)
    reference = Reference(var)

    # lhs is not an array
    assignment = Assignment.create(reference, one)
    assert assignment.is_array_range is False

    # lhs is an array reference but has no range
    array_type = ArrayType(REAL_TYPE, [10, 10])
    symbol = DataSymbol("y", array_type)
    array_ref = Reference(symbol)
    assignment = Assignment.create(array_ref, one.copy())
    assert assignment.is_array_range is False

    # lhs is an array reference but the single index value is obtained
    # using an array range, y(1, SUM(map(:), 1)) = 1.0
    int_array_type = ArrayType(INTEGER_SINGLE_TYPE, [10])
    map_sym = DataSymbol("map", int_array_type)
    start = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                   Reference(map_sym), int_one.copy())
    stop = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                  Reference(map_sym), int_one.copy())
    my_range = Range.create(start, stop)
    sum_op = BinaryOperation.create(BinaryOperation.Operator.SUM,
                                    ArrayReference.create(map_sym, [my_range]),
                                    int_one.copy())
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), sum_op]), one.copy())
    assert assignment.is_array_range is False

    # When the slice has two operator ancestors, one of which is a reduction
    # e.g y(1, SUM(ABS(map(:)), 1)) = 1.0
    abs_op = UnaryOperation.create(
        UnaryOperation.Operator.ABS,
        ArrayReference.create(map_sym, [my_range.copy()]))
    sum_op2 = BinaryOperation.create(BinaryOperation.Operator.SUM, abs_op,
                                     int_one.copy())
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), sum_op2]), one.copy())
    assert assignment.is_array_range is False

    # lhs is a scalar member of a structure
    grid_type = StructureType.create([
        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC),
        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC)
    ])
    grid_type_symbol = DataTypeSymbol("grid_type", grid_type)
    grid_sym = DataSymbol("grid", grid_type_symbol)
    assignment = Assignment.create(StructureReference.create(grid_sym, ["dx"]),
                                   one.copy())
    assert assignment.is_array_range is False
Beispiel #7
0
def test_binaryoperation_node_str():
    ''' Check the node_str method of the Binary Operation class.'''
    binary_operation = BinaryOperation(BinaryOperation.Operator.ADD)
    op1 = Literal("1", INTEGER_SINGLE_TYPE)
    op2 = Literal("1", INTEGER_SINGLE_TYPE)
    binary_operation.addchild(op1)
    binary_operation.addchild(op2)
    coloredtext = colored("BinaryOperation", BinaryOperation._colour)
    assert coloredtext+"[operator:'ADD']" in binary_operation.node_str()
Beispiel #8
0
def test_binaryoperation_initialization():
    ''' Check the initialization method of the BinaryOperation class works
    as expected.'''

    with pytest.raises(TypeError) as err:
        _ = BinaryOperation("not an operator")
    assert "BinaryOperation operator argument must be of type " \
           "BinaryOperation.Operator but found" in str(err.value)
    bop = BinaryOperation(BinaryOperation.Operator.ADD)
    assert bop._operator is BinaryOperation.Operator.ADD
Beispiel #9
0
def test_binaryoperation_node_str():
    ''' Check the node_str method of the Binary Operation class.'''
    from psyclone.psyir.nodes.node import colored, SCHEDULE_COLOUR_MAP
    binary_operation = BinaryOperation(BinaryOperation.Operator.ADD)
    op1 = Literal("1", INTEGER_SINGLE_TYPE, parent=binary_operation)
    op2 = Literal("1", INTEGER_SINGLE_TYPE, parent=binary_operation)
    binary_operation.addchild(op1)
    binary_operation.addchild(op2)
    coloredtext = colored("BinaryOperation", SCHEDULE_COLOUR_MAP["Operation"])
    assert coloredtext + "[operator:'ADD']" in binary_operation.node_str()
Beispiel #10
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
Beispiel #11
0
def test_cw_binaryoperator():
    '''Check the CWriter class binary_operation method correctly
    prints out the C representation of any given BinaryOperation.

    '''
    cwriter = CWriter()

    # Test UnaryOperation without children.
    binary_operation = BinaryOperation(BinaryOperation.Operator.ADD)
    with pytest.raises(VisitorError) as err:
        _ = cwriter(binary_operation)
    assert ("BinaryOperation malformed or incomplete. It should have "
            "exactly 2 children, but found 0." in str(err.value))

    # Test with children
    ref1 = Reference(DataSymbol("a", REAL_TYPE))
    ref2 = Reference(DataSymbol("b", REAL_TYPE))
    binary_operation = BinaryOperation.create(BinaryOperation.Operator.ADD,
                                              ref1, ref2)
    assert cwriter(binary_operation) == '(a + b)'

    # Test all supported Operators
    test_list = ((BinaryOperation.Operator.ADD,
                  '(a + b)'), (BinaryOperation.Operator.SUB, '(a - b)'),
                 (BinaryOperation.Operator.MUL,
                  '(a * b)'), (BinaryOperation.Operator.DIV, '(a / b)'),
                 (BinaryOperation.Operator.REM,
                  '(a % b)'), (BinaryOperation.Operator.POW, 'pow(a, b)'),
                 (BinaryOperation.Operator.EQ,
                  '(a == b)'), (BinaryOperation.Operator.NE, '(a != b)'),
                 (BinaryOperation.Operator.GT,
                  '(a > b)'), (BinaryOperation.Operator.GE, '(a >= b)'),
                 (BinaryOperation.Operator.LT,
                  '(a < b)'), (BinaryOperation.Operator.LE, '(a <= b)'),
                 (BinaryOperation.Operator.AND,
                  '(a && b)'), (BinaryOperation.Operator.OR,
                                '(a || b)'), (BinaryOperation.Operator.SIGN,
                                              'copysign(a, b)'))

    for operator, expected in test_list:
        binary_operation._operator = operator
        assert cwriter(binary_operation) == expected

    # Test that an unsupported operator raises a error
    class Unsupported(object):
        '''Dummy class'''
        def __init__(self):
            pass

    binary_operation._operator = Unsupported
    with pytest.raises(VisitorError) as err:
        _ = cwriter(binary_operation)
    assert "The C backend does not support the '" in str(err.value)
    assert "' operator." in str(err.value)
Beispiel #12
0
def test_correct_2min(tmpdir):
    '''Check that a valid example produces the expected output when there
    is more than one MIN() in an expression.

    '''
    Config.get().api = "nemo"
    operation = example_psyir_binary(lambda arg: arg)
    root = operation.root
    assignment = operation.parent
    operation.detach()
    min_op = BinaryOperation.create(BinaryOperation.Operator.MIN,
                                    Literal("1.0", REAL_TYPE),
                                    Literal("2.0", REAL_TYPE))
    op1 = BinaryOperation.create(BinaryOperation.Operator.ADD, min_op,
                                 operation)
    assignment.addchild(op1)

    writer = FortranWriter()
    result = writer(root)
    assert ("subroutine min_example(arg, arg_1)\n"
            "  real, intent(inout) :: arg\n"
            "  real, intent(inout) :: arg_1\n"
            "  real :: psyir_tmp\n\n"
            "  psyir_tmp = MIN(1.0, 2.0) + MIN(arg, arg_1)\n\n"
            "end subroutine min_example\n") in result
    trans = Min2CodeTrans()
    trans.apply(operation, root.symbol_table)
    trans.apply(min_op, root.symbol_table)
    result = writer(root)
    assert ("subroutine min_example(arg, arg_1)\n"
            "  real, intent(inout) :: arg\n"
            "  real, intent(inout) :: arg_1\n"
            "  real :: psyir_tmp\n"
            "  real :: res_min\n"
            "  real :: tmp_min\n"
            "  real :: res_min_1\n"
            "  real :: tmp_min_1\n\n"
            "  res_min = arg\n"
            "  tmp_min = arg_1\n"
            "  if (tmp_min < res_min) then\n"
            "    res_min = tmp_min\n"
            "  end if\n"
            "  res_min_1 = 1.0\n"
            "  tmp_min_1 = 2.0\n"
            "  if (tmp_min_1 < res_min_1) then\n"
            "    res_min_1 = tmp_min_1\n"
            "  end if\n"
            "  psyir_tmp = res_min_1 + res_min\n\n"
            "end subroutine min_example\n") in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
Beispiel #13
0
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_bound_intrinsic_wrong_type(bound):
    ''' Check that attempting to create an L/UBOUND intrinsic operator
    with the wrong type of arguments raises the expected error. '''
    with pytest.raises(TypeError) as err:
        # First argument must be an Array
        _ = BinaryOperation.create(bound, Literal("1", INTEGER_TYPE),
                                   Literal("1", INTEGER_TYPE))
    assert "must be an Array but got: 'Literal" in str(err.value)
    with pytest.raises(TypeError) as err:
        # Second argument cannot be a real literal
        _ = BinaryOperation.create(bound, Array("array"),
                                   Literal("1.0", REAL_TYPE))
    assert ("must be an integer but got a Literal of type REAL"
            in str(err.value))
Beispiel #15
0
def test_binaryoperation_can_be_printed():
    '''Test that a Binary Operation instance can always be printed (i.e. is
    initialised fully)'''
    binary_operation = BinaryOperation(BinaryOperation.Operator.ADD)
    assert "BinaryOperation[operator:'ADD']" in str(binary_operation)
    op1 = Literal("1", INTEGER_SINGLE_TYPE)
    op2 = Literal("2", INTEGER_SINGLE_TYPE)
    binary_operation.addchild(op1)
    binary_operation.addchild(op2)
    # Check the node children are also printed
    assert ("Literal[value:'1', Scalar<INTEGER, SINGLE>]\n"
            in str(binary_operation))
    assert ("Literal[value:'2', Scalar<INTEGER, SINGLE>]"
            in str(binary_operation))
Beispiel #16
0
def test_binaryoperation_operator():
    '''Test that the operator property returns the binaryoperator in the
    binaryoperation.

    '''
    binary_operation = BinaryOperation(BinaryOperation.Operator.ADD)
    assert binary_operation.operator == BinaryOperation.Operator.ADD
def test_validate_intrinsic():
    '''Check that the validate method returns an exception if the rhs of
    the assignment contains an operator that only returns an array
    i.e. can't be performed elementwise. At the moment MATMUL is the
    only operator of this type.

    '''
    symbol_table = SymbolTable()
    array_x = create_array_x(symbol_table)
    array_y_2 = create_array_y_2d_slice(symbol_table)
    matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, array_y_2,
                                    array_x)
    reference = ArrayReference.create(
        symbol_table.lookup("x"), [create_range(symbol_table.lookup("x"), 1)])
    assignment = Assignment.create(reference, matmul)

    trans = ArrayRange2LoopTrans()
    with pytest.raises(TransformationError) as info:
        trans.validate(assignment)
    assert ("Error in ArrayRange2LoopTrans transformation. The rhs of the "
            "supplied Assignment node 'BinaryOperation[operator:'MATMUL']\n"
            "ArrayReference[name:'y2']\nRange[]\nRange[]\n\n"
            "ArrayReference[name:'x']\nRange[]\n' contains the "
            "MATMUL operator which can't be performed elementwise."
            in str(info.value))
Beispiel #18
0
def test_arraytype():
    '''Test that the ArrayType class __init__ works as expected. Test the
    different dimension datatypes that are supported.'''
    scalar_type = ScalarType(ScalarType.Intrinsic.INTEGER, 4)
    data_symbol = DataSymbol("var", scalar_type, constant_value=30)
    one = Literal("1", scalar_type)
    var_plus_1 = BinaryOperation.create(
        BinaryOperation.Operator.ADD, Reference(data_symbol), one)
    literal = Literal("20", scalar_type)
    array_type = ArrayType(
        scalar_type, [10, literal, var_plus_1, Reference(data_symbol),
                      ArrayType.Extent.DEFERRED, ArrayType.Extent.ATTRIBUTE])
    assert isinstance(array_type, ArrayType)
    assert len(array_type.shape) == 6
    # Provided as an int but stored as a Literal
    shape0 = array_type.shape[0]
    assert isinstance(shape0, Literal)
    assert shape0.value == "10"
    assert shape0.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
    assert shape0.datatype.precision == ScalarType.Precision.UNDEFINED
    # Provided and stored as a Literal (DataNode)
    assert array_type.shape[1] is literal
    # Provided and stored as an Operator (DataNode)
    assert array_type.shape[2] is var_plus_1
    # Provided and stored as a Reference to a DataSymbol
    assert isinstance(array_type.shape[3], Reference)
    assert array_type.shape[3].symbol is data_symbol
    # Provided and stored as a deferred extent
    assert array_type.shape[4] == ArrayType.Extent.DEFERRED
    # Provided as an attribute extent
    assert array_type.shape[5] == ArrayType.Extent.ATTRIBUTE
Beispiel #19
0
def test_datasymbol_constant_value_setter():
    '''Test that a DataSymbol constant value can be set if given a new valid
    constant value.'''

    # Test with valid constant values
    sym = DataSymbol('a', INTEGER_SINGLE_TYPE, constant_value=7)
    assert sym.constant_value.value == "7"
    sym.constant_value = 9
    assert sym.constant_value.value == "9"

    sym = DataSymbol('a', REAL_SINGLE_TYPE, constant_value=3.1415)
    assert sym.constant_value.value == "3.1415"
    sym.constant_value = 1.0
    assert sym.constant_value.value == "1.0"

    sym = DataSymbol('a', BOOLEAN_TYPE, constant_value=True)
    assert sym.constant_value.value == "true"
    sym.constant_value = False
    assert sym.constant_value.value == "false"

    # Test with valid constant expressions
    lhs = Literal('2', INTEGER_SINGLE_TYPE)
    rhs = Reference(DataSymbol('constval', INTEGER_SINGLE_TYPE))
    ct_expr = BinaryOperation.create(BinaryOperation.Operator.ADD, lhs, rhs)
    sym = DataSymbol('a', INTEGER_SINGLE_TYPE, constant_value=ct_expr)
    assert isinstance(sym.constant_value, BinaryOperation)
    assert sym.constant_value is ct_expr
Beispiel #20
0
def example_psyir_binary(create_expression):
    '''Utility function that creates a PSyIR tree containing a binary MIN
    intrinsic operator and returns the operator.

    :param function create_expresssion: function used to create the \
        content of the first argument of the MIN operator.

    :returns: PSyIR MIN operator instance.
    :rtype: :py:class:`psyclone.psyGen.BinaryOperation`

    '''
    symbol_table = SymbolTable()
    arg1 = symbol_table.new_symbol("arg",
                                   symbol_type=DataSymbol,
                                   datatype=REAL_TYPE,
                                   interface=ArgumentInterface(
                                       ArgumentInterface.Access.READWRITE))
    arg2 = symbol_table.new_symbol("arg",
                                   symbol_type=DataSymbol,
                                   datatype=REAL_TYPE,
                                   interface=ArgumentInterface(
                                       ArgumentInterface.Access.READWRITE))
    arg3 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE)
    symbol_table.specify_argument_list([arg1, arg2])
    var1 = Reference(arg1)
    var2 = Reference(arg2)
    var3 = Reference(arg3)
    oper = BinaryOperation.Operator.MIN
    operation = BinaryOperation.create(oper, create_expression(var1), var2)
    assign = Assignment.create(var3, operation)
    _ = KernelSchedule.create("min_example", symbol_table, [assign])
    return operation
Beispiel #21
0
def test_reference_accesses_bounds(operator_type):
    '''Test that the reference_accesses method behaves as expected when
    the reference is the first argument to either the lbound or ubound
    intrinsic as that is simply looking up the array bounds (therefore
    var_access_info should be empty) and when the reference is the
    second argument of either the lbound or ubound intrinsic (in which
    case the access should be a read).

    '''
    # Note, one would usually expect UBOUND to provide the upper bound
    # of a range but to simplify the test both LBOUND and UBOUND are
    # used for the lower bound. This does not affect the test.
    one = Literal("1", INTEGER_TYPE)
    array_symbol = DataSymbol("test", ArrayType(REAL_TYPE, [10]))
    array_ref1 = Reference(array_symbol)
    array_ref2 = Reference(array_symbol)
    array_access = Array.create(array_symbol, [one])

    # test when first or second argument to LBOUND or UBOUND is an
    # array reference
    operator = BinaryOperation.create(operator_type, array_ref1, array_ref2)
    array_access.children[0] = Range.create(operator, one, one)
    var_access_info = VariablesAccessInfo()
    array_ref1.reference_accesses(var_access_info)
    assert str(var_access_info) == ""
    var_access_info = VariablesAccessInfo()
    array_ref2.reference_accesses(var_access_info)
    assert str(var_access_info) == "test: READ"
def test_real_binaryop_invalid():
    ''' Test that the create method rejects invalid precisions. '''
    sym = DataSymbol("tmp1", REAL_SINGLE_TYPE)
    oper = BinaryOperation.Operator.REAL
    with pytest.raises(TypeError) as err:
        _ = BinaryOperation.create(oper, Reference(sym),
                                   Literal("1.0", REAL_SINGLE_TYPE))
    assert ("Precision argument to REAL operation must be specified using a "
            "DataSymbol, ScalarType.PRECISION or integer Literal but got "
            "xxxx" in str(err.value))
    # A Symbol of REAL type cannot be used to specify a precision
    wrong_kind = DataSymbol("not_wp", REAL_SINGLE_TYPE)
    with pytest.raises(TypeError) as err:
        _ = BinaryOperation.create(oper, Reference(sym), Reference(wrong_kind))
    assert ("If the precision argument to a REAL operation is a Reference "
            "then it must be to a symbol of integer type but got: 'yyyy'" in
            str(err.value))
Beispiel #23
0
def _get_array_bound(array, index):
    '''A utility function that returns the appropriate loop bounds (lower,
    upper and step) for a particular index of an array reference. At
    the moment all entries for the index are assumed to be accessed.
    If the array symbol is declared with known bounds (an integer or a
    symbol) then these bound values are used. If the size is unknown
    (a deferred or attribute type) then the LBOUND and UBOUND PSyIR
    nodes are used.

    :param array: the reference that we are interested in.
    :type array: :py:class:`psyir.nodes.Reference`
    :param int index: the (array) reference index that we are \
        interested in.
    :returns: the loop bounds for this array index.
    :rtype: (Literal, Literal, Literal) or \
        (BinaryOperation, BinaryOperation, Literal)

    :raises TransformationError: if the shape of the array's symbol is \
        not supported.

    '''
    # Added import here to avoid circular dependencies.
    # pylint: disable=import-outside-toplevel
    from psyclone.psyir.transformations import TransformationError

    my_dim = array.symbol.shape[index]
    if isinstance(my_dim, DataNode):
        lower_bound = Literal("1", INTEGER_TYPE)
        upper_bound = my_dim
    elif isinstance(my_dim, DataSymbol):
        lower_bound = Literal("1", INTEGER_TYPE)
        upper_bound = Reference(my_dim)
    elif my_dim in [ArrayType.Extent.DEFERRED, ArrayType.Extent.ATTRIBUTE]:
        lower_bound = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                             Reference(array.symbol),
                                             Literal(str(index), INTEGER_TYPE))
        upper_bound = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                             Reference(array.symbol),
                                             Literal(str(index), INTEGER_TYPE))
    else:
        raise TransformationError(
            "Unsupported index type '{0}' found for dimension {1} of array "
            "'{2}'.".format(type(my_dim).__name__, index + 1, array.name))
    step = Literal("1", INTEGER_TYPE)
    return (lower_bound, upper_bound, step)
Beispiel #24
0
def test_array_is_lower_bound():
    '''Test that the is_lower_bound method in the Array Node works as
    expected.

    '''
    one = Literal("1", INTEGER_TYPE)
    array = ArrayReference.create(DataSymbol("test",
                                             ArrayType(REAL_TYPE, [10])),
                                  [one])
    with pytest.raises(TypeError) as info:
        array.is_lower_bound("hello")
    assert ("The index argument should be an integer but found 'str'."
            in str(info.value))

    # not a range node at index 0
    assert not array.is_lower_bound(0)

    # range node does not have a binary operator for its start value
    array.children[0] = Range.create(one, one, one)
    assert not array.is_lower_bound(0)

    # range node lbound references a different array
    array2 = ArrayReference.create(DataSymbol("test2",
                                              ArrayType(REAL_TYPE, [10])),
                                   [one])
    operator = BinaryOperation.create(
        BinaryOperation.Operator.LBOUND, array2,
        Literal("1", INTEGER_TYPE))
    array.children[0] = Range.create(operator, one, one)
    assert not array.is_lower_bound(0)

    # range node lbound references a different index
    operator = BinaryOperation.create(
        BinaryOperation.Operator.LBOUND, array,
        Literal("2", INTEGER_TYPE))
    array.children[0] = Range.create(operator, one, one)
    assert not array.is_lower_bound(0)

    # all is well
    operator = BinaryOperation.create(
        BinaryOperation.Operator.LBOUND, array, one)
    array.children[0] = Range.create(operator, one, one)
    assert array.is_lower_bound(0)
def test_validate5():
    '''Check that the Matmul2Code validate method raises the expected
    exception when the supplied node is a MATMUL binary operation but
    either or both arguments are not references.

    '''
    trans = Matmul2CodeTrans()
    array_type = ArrayType(REAL_TYPE, [10])
    array = Array.create(DataSymbol("x", array_type),
                         [Literal("10", INTEGER_TYPE)])
    mult = BinaryOperation.create(BinaryOperation.Operator.MUL, array, array)
    matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, mult,
                                    mult)
    _ = Assignment.create(array, matmul)
    with pytest.raises(TransformationError) as excinfo:
        trans.validate(matmul)
    assert ("Expected children of a MATMUL BinaryOperation to be references, "
            "but found 'BinaryOperation', 'BinaryOperation'."
            in str(excinfo.value))
Beispiel #26
0
def test_correct_expr(tmpdir):
    '''Check that a valid example produces the expected output when MIN()
    is part of an expression.

    '''
    Config.get().api = "nemo"
    operation = example_psyir_binary(lambda arg: arg)
    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 min_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 + MIN(arg, arg_1) + 2.0\n\n"
            "end subroutine min_example\n") in result
    trans = Min2CodeTrans()
    trans.apply(operation, operation.root.symbol_table)
    result = writer(operation.root)
    assert ("subroutine min_example(arg,arg_1)\n"
            "  real, intent(inout) :: arg\n"
            "  real, intent(inout) :: arg_1\n"
            "  real :: psyir_tmp\n"
            "  real :: res_min\n"
            "  real :: tmp_min\n\n"
            "  res_min=arg\n"
            "  tmp_min=arg_1\n"
            "  if (tmp_min < res_min) then\n"
            "    res_min=tmp_min\n"
            "  end if\n"
            "  psyir_tmp=1.0 + res_min + 2.0\n\n"
            "end subroutine min_example\n") in result
    assert Compile(tmpdir).string_compiles(result)
    # Remove the created config instance
    Config._instance = None
def test_validate4():
    '''Check that the Matmul2Code validate method raises the expected
    exception when the supplied node is a MATMUL binary operation but
    it is not the only operation on the RHS of an assignment.

    '''
    trans = Matmul2CodeTrans()
    vector_type = ArrayType(REAL_TYPE, [10])
    array_type = ArrayType(REAL_TYPE, [10, 10])
    vector = Reference(DataSymbol("x", vector_type))
    array = Reference(DataSymbol("y", array_type))
    matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, array,
                                    vector)
    rhs = BinaryOperation.create(BinaryOperation.Operator.MUL, matmul, vector)
    _ = Assignment.create(array, rhs)
    with pytest.raises(TransformationError) as excinfo:
        trans.validate(matmul)
    assert ("Transformation Error: Matmul2CodeTrans only supports the "
            "transformation of a MATMUL operation when it is the sole "
            "operation on the rhs of an assignment." in str(excinfo.value))
Beispiel #28
0
def test_string_compare():
    '''Check that the string_compare utility function in
    ArrayRange2LoopTrans works as expected.

    '''
    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.string_compare(None, None)
    assert ("The first argument to the string_compare method should be a Node "
            "but found 'NoneType'." in str(info.value))

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.string_compare(Node(), None)
    assert (
        "The second argument to the string_compare method should be a Node "
        "but found 'NoneType'." in str(info.value))

    node1 = Literal("1.0", REAL_TYPE)
    node2 = BinaryOperation.create(BinaryOperation.Operator.MUL, node1, node1)
    node3 = BinaryOperation.create(BinaryOperation.Operator.MAX, node2, node2)
    assert ArrayRange2LoopTrans.string_compare(node3, node3) is True
    assert ArrayRange2LoopTrans.string_compare(node3, node2) is False
Beispiel #29
0
def create_range(array_symbol, dim):
    '''Utility routine that creates and returns a Range Node that
    specifies the full range of the supplied dimension (dim) in the
    array (array_symbol). This is done using the LBOUND and UBOUND
    intrinsics.

    :param array_symbol: the array of interest.
    :type array_symbol: :py:class:`psyclone.psyir.symbol.DataSymbol`
    :param int dim: the dimension of interest in the array.

    :returns: a range node specifying the full range of the supplied \
        array dimension.
    :rtype: :py:class:`psyclone.psyir.nodes.Range`

    '''
    int_dim = Literal(str(dim), INTEGER_TYPE)
    lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                    Reference(array_symbol), int_dim)
    ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                    Reference(array_symbol), int_dim)
    return Range.create(lbound, ubound)
Beispiel #30
0
def test_binaryoperation_create():
    '''Test that the create method in the BinaryOperation class correctly
    creates a BinaryOperation instance.

    '''
    lhs = Reference(DataSymbol("tmp1", REAL_SINGLE_TYPE))
    rhs = Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE))
    oper = BinaryOperation.Operator.ADD
    binaryoperation = BinaryOperation.create(oper, lhs, rhs)
    check_links(binaryoperation, [lhs, rhs])
    result = FortranWriter().binaryoperation_node(binaryoperation)
    assert result == "tmp1 + tmp2"