def test_ifblock_create_invalid(): '''Test that the create method in an IfBlock class raises the expected exception if the provided input is invalid. ''' if_condition = Literal('true', BOOLEAN_TYPE) if_body = [Assignment.create( Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), Assignment.create( Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE))] # if_condition not a Node. with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create("True", if_body) assert ("Item 'str' can't be child 0 of 'If'. The valid format is: " "'DataNode, Schedule [, Schedule]'.") in str(excinfo.value) # One or more if body not a Node. if_body_err = [Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), "invalid"] with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, if_body_err) assert ("Item 'str' can't be child 1 of 'Schedule'. The valid format is: " "'[Statement]*'.") in str(excinfo.value) # If body not a list. with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, "invalid") assert ("if_body argument in create method of IfBlock class should be a " "list.") in str(excinfo.value) # One of more of else_body not a Node. if_condition = Literal('true', BOOLEAN_TYPE) if_body = [Assignment.create( Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), Assignment.create( Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE))] else_body_err = [Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE)), "invalid"] with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, if_body, else_body_err) assert ("Item 'str' can't be child 1 of 'Schedule'. The valid format is: " "'[Statement]*'.") in str(excinfo.value) # Else body not a list. with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, if_body, "invalid") assert ("else_body argument in create method of IfBlock class should be a " "list.") in str(excinfo.value)
def test_ifblock_create(): '''Test that the create method in an IfBlock class correctly creates an IfBlock instance. ''' # Without an else clause. if_condition = Literal('true', BOOLEAN_TYPE) if_body = [Assignment.create( Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), Assignment.create( Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE))] ifblock = IfBlock.create(if_condition, if_body) if_schedule = ifblock.children[1] assert isinstance(if_schedule, Schedule) check_links(ifblock, [if_condition, if_schedule]) check_links(if_schedule, if_body) result = FortranWriter().ifblock_node(ifblock) assert result == ("if (.true.) then\n" " tmp = 0.0\n" " tmp2 = 1.0\n" "end if\n") # With an else clause. if_condition = Literal('true', BOOLEAN_TYPE) if_body = [Assignment.create( Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), Assignment.create( Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE))] else_body = [Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE)), Assignment.create(Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE))] ifblock = IfBlock.create(if_condition, if_body, else_body) if_schedule = ifblock.children[1] assert isinstance(if_schedule, Schedule) else_schedule = ifblock.children[2] assert isinstance(else_schedule, Schedule) check_links(ifblock, [if_condition, if_schedule, else_schedule]) check_links(if_schedule, if_body) check_links(else_schedule, else_body) result = FortranWriter().ifblock_node(ifblock) assert result == ("if (.true.) then\n" " tmp = 0.0\n" " tmp2 = 1.0\n" "else\n" " tmp = 1.0\n" " tmp2 = 0.0\n" "end if\n")
def test_ifblock_invalid_annotation(): ''' Test that initialising IfBlock with invalid annotations produce the expected error.''' with pytest.raises(InternalError) as err: _ = IfBlock(annotations=["invalid"]) assert ("IfBlock with unrecognized annotation 'invalid', valid " "annotations are:") in str(err.value)
def test_ifblock_can_be_printed(): '''Test that an IfBlock instance can always be printed (i.e. is initialised fully)''' condition = Reference(DataSymbol('condition1', BOOLEAN_TYPE)) then_content = [Return()] ifblock = IfBlock.create(condition, then_content) assert "If[]\n" in str(ifblock) assert "condition1" in str(ifblock) # Test condition is printed assert "Return[]" in str(ifblock) # Test if_body is printed
def test_ifblock_view_indices(capsys): ''' Check that the view method only displays indices on the nodes in the body (and else body) of an IfBlock. ''' colouredif = colored("If", IfBlock._colour) colouredreturn = colored("Return", Return._colour) colouredref = colored("Reference", Reference._colour) condition = Reference(DataSymbol('condition1', REAL_SINGLE_TYPE)) then_content = [Return()] ifblock = IfBlock.create(condition, then_content) ifblock.view() output, _ = capsys.readouterr() # Check that we only prepend child indices where it makes sense assert colouredif + "[]" in output assert "0: " + colouredreturn in output assert ": " + colouredref not in output
def test_ifblock_node_str(): ''' Check the node_str method of the IfBlock class.''' colouredif = colored("If", IfBlock._colour) ifblock = IfBlock() output = ifblock.node_str() assert colouredif+"[]" in output ifblock = IfBlock(annotations=['was_elseif']) output = ifblock.node_str() assert colouredif+"[annotations='was_elseif']" in output
def test_ifblock_node_str(): ''' Check the node_str method of the IfBlock class.''' from psyclone.psyir.nodes.node import colored, SCHEDULE_COLOUR_MAP colouredif = colored("If", SCHEDULE_COLOUR_MAP["If"]) ifblock = IfBlock() output = ifblock.node_str() assert colouredif + "[]" in output ifblock = IfBlock(annotations=['was_elseif']) output = ifblock.node_str() assert colouredif + "[annotations='was_elseif']" in output
def test_cw_ifblock(): '''Check the CWriter class ifblock method correctly prints out the C representation. ''' from psyclone.psyir.nodes import IfBlock # Try with just an IfBlock node ifblock = IfBlock() cwriter = CWriter() with pytest.raises(VisitorError) as err: _ = cwriter(ifblock) assert ("IfBlock malformed or incomplete. It should have " "at least 2 children, but found 0." in str(err.value)) # Add the if condition ifblock.addchild(Reference(DataSymbol('a', REAL_TYPE), parent=ifblock)) with pytest.raises(VisitorError) as err: _ = cwriter(ifblock) assert ("IfBlock malformed or incomplete. It should have " "at least 2 children, but found 1." in str(err.value)) # Fill the if_body and else_body ifblock.addchild(Schedule(parent=ifblock)) ifblock.addchild(Schedule(parent=ifblock)) ifblock.if_body.addchild(Return(parent=ifblock.if_body)) condition = Reference(DataSymbol('b', REAL_TYPE)) then_content = [Return()] else_content = [Return()] ifblock2 = IfBlock.create(condition, then_content, else_content) ifblock2.parent = ifblock.if_body ifblock.else_body.addchild(ifblock2) result = cwriter(ifblock) assert result == ("if (a) {\n" " return;\n" "} else {\n" " if (b) {\n" " return;\n" " } else {\n" " return;\n" " }\n" "}\n")
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_ifblock_properties(): '''Test that an IfBlock node properties can be retrieved''' ifblock = IfBlock() # Condition can't be retrieved before it is added as a child. with pytest.raises(InternalError) as err: _ = ifblock.condition assert("IfBlock malformed or incomplete. It should have " "at least 2 children, but found 0." in str(err.value)) ref1 = Reference(DataSymbol('condition1', BOOLEAN_TYPE), parent=ifblock) ifblock.addchild(ref1) # If_body can't be retrieved before is added as a child. with pytest.raises(InternalError) as err: _ = ifblock.if_body assert("IfBlock malformed or incomplete. It should have " "at least 2 children, but found 1." in str(err.value)) sch = Schedule() ifblock.addchild(sch) ret = Return() sch.addchild(ret) # Now we can retrieve the condition and the if_body, but else is empty assert ifblock.condition is ref1 assert ifblock.if_body[0] is ret assert not ifblock.else_body sch2 = Schedule() ifblock.addchild(sch2) ret2 = Return() sch2.addchild(ret2) # Now we can retrieve else_body assert ifblock.else_body[0] is ret2
def apply(self, node, options=None): '''Apply the MIN intrinsic conversion transformation to the specified node. This node must be an MIN NaryOperation. The MIN NaryOperation is converted to equivalent inline code. This is implemented as a PSyIR transform from: .. code-block:: python R = ... MIN(A, B, C ...) ... to: .. code-block:: python res_min = A tmp_min = B IF tmp_min < res_min: res_min = tmp_min tmp_min = C IF tmp_min < res_min: res_min = tmp_min ... R = ... res_min ... where ``A``, ``B``, ``C`` ... could be arbitrarily complex PSyIR expressions and the ``...`` before and after ``MIN(A, B, C ...)`` can be arbitrary PSyIR code. 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 MIN Binary- or Nary-Operation node. :type node: :py:class:`psyclone.psyGen.BinaryOperation` or \ :py:class:`psyclone.psyGen.NaryOperation` :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 a temporary result variable. There is an assumption # here that the MIN 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 # appropriate methods to query nodes (see #658). res_var_symbol = symbol_table.new_symbol( "res_min", symbol_type=DataSymbol, datatype=REAL_TYPE) # Create a temporary variable. Again there is an # assumption here about the datatype - please see previous # comment (associated issue #658). tmp_var_symbol = symbol_table.new_symbol( "tmp_min", symbol_type=DataSymbol, datatype=REAL_TYPE) # Replace operation with a temporary (res_var). oper_parent.children[node.position] = Reference(res_var_symbol, parent=oper_parent) # res_var=A lhs = Reference(res_var_symbol) new_assignment = Assignment.create(lhs, node.children[0]) new_assignment.parent = assignment.parent assignment.parent.children.insert(assignment.position, new_assignment) # For each of the remaining min arguments (B,C...) for expression in node.children[1:]: # tmp_var=(B or C or ...) lhs = Reference(tmp_var_symbol) new_assignment = Assignment.create(lhs, expression) new_assignment.parent = assignment.parent assignment.parent.children.insert(assignment.position, new_assignment) # if_condition: tmp_var<res_var lhs = Reference(tmp_var_symbol) rhs = Reference(res_var_symbol) if_condition = BinaryOperation.create(BinaryOperation.Operator.LT, lhs, rhs) # then_body: res_var=tmp_var lhs = Reference(res_var_symbol) rhs = Reference(tmp_var_symbol) then_body = [Assignment.create(lhs, rhs)] # if [if_condition] then [then_body] if_stmt = IfBlock.create(if_condition, then_body) if_stmt.parent = assignment.parent assignment.parent.children.insert(assignment.position, if_stmt)
def test_ifblock_children_validation(): '''Test that children added to IfBlock are validated. IfBlock accepts DataNodes for the children 0 to 2 and a Shcedule for child 3. ''' ifblock = IfBlock() if_condition = Literal('true', BOOLEAN_TYPE) if_body = Schedule() else_body = Schedule() # First child with pytest.raises(GenerationError) as excinfo: ifblock.addchild(if_body) assert ("Item 'Schedule' can't be child 0 of 'If'. The valid format is: " "'DataNode, Schedule [, Schedule]'." in str(excinfo.value)) ifblock.addchild(if_condition) # Second child with pytest.raises(GenerationError) as excinfo: ifblock.addchild(if_condition) assert ("Item 'Literal' can't be child 1 of 'If'. The valid format is: " "'DataNode, Schedule [, Schedule]'." in str(excinfo.value)) ifblock.addchild(if_body) # Third child with pytest.raises(GenerationError) as excinfo: ifblock.addchild(if_condition) assert ("Item 'Literal' can't be child 2 of 'If'. The valid format is: " "'DataNode, Schedule [, Schedule]'." in str(excinfo.value)) ifblock.addchild(else_body) # Additional children with pytest.raises(GenerationError) as excinfo: ifblock.addchild(else_body) assert ("Item 'Schedule' can't be child 3 of 'If'. The valid format is: " "'DataNode, Schedule [, Schedule]'." in str(excinfo.value))
def apply(self, node, options=None): '''Apply this transformation to the supplied node. :param node: the node to transform. :type node: :py:class:`psyclone.gocean1p0.GOKern` :param options: a dictionary with options for transformations. :type options: dict of string:values or None :returns: 2-tuple of new schedule and memento of transform. :rtype: (:py:class:`psyclone.gocean1p0.GOInvokeSchedule`, \ :py:class:`psyclone.undoredo.Memento`) ''' self.validate(node, options) # Get useful references invoke_st = node.ancestor(InvokeSchedule).symbol_table inner_loop = node.ancestor(Loop) outer_loop = inner_loop.ancestor(Loop) cursor = outer_loop.position # Make sure the boundary symbols in the PSylayer exist inv_xstart = invoke_st.symbol_from_tag("xstart_" + node.name, root_name="xstart", symbol_type=DataSymbol, datatype=INTEGER_TYPE) inv_xstop = invoke_st.symbol_from_tag("xstop_" + node.name, root_name="xstop", symbol_type=DataSymbol, datatype=INTEGER_TYPE) inv_ystart = invoke_st.symbol_from_tag("ystart_" + node.name, root_name="ystart", symbol_type=DataSymbol, datatype=INTEGER_TYPE) inv_ystop = invoke_st.symbol_from_tag("ystop_" + node.name, root_name="ystop", symbol_type=DataSymbol, datatype=INTEGER_TYPE) # If the kernel acts on the whole iteration space, the boundary values # are not needed. This also avoids adding duplicated arguments if this # transformation is applied more than once to the same kernel. But the # declaration and initialisation above still needs to exist because the # boundary variables are expected to exist by the generation code. if (inner_loop.field_space == "go_every" and outer_loop.field_space == "go_every" and inner_loop.iteration_space == "go_all_pts" and outer_loop.iteration_space == "go_all_pts"): return node.root, None # Initialise the boundary values provided by the Loop construct assign1 = Assignment.create(Reference(inv_xstart), inner_loop.lower_bound().copy()) outer_loop.parent.children.insert(cursor, assign1) cursor = cursor + 1 assign2 = Assignment.create(Reference(inv_xstop), inner_loop.upper_bound().copy()) outer_loop.parent.children.insert(cursor, assign2) cursor = cursor + 1 assign3 = Assignment.create(Reference(inv_ystart), outer_loop.lower_bound().copy()) outer_loop.parent.children.insert(cursor, assign3) cursor = cursor + 1 assign4 = Assignment.create(Reference(inv_ystop), outer_loop.upper_bound().copy()) outer_loop.parent.children.insert(cursor, assign4) # Update Kernel Call argument list for symbol in [inv_xstart, inv_xstop, inv_ystart, inv_ystop]: node.arguments.append(symbol.name, "go_i_scalar") # Now that the boundaries are inside the kernel, the looping should go # through all the field points inner_loop.field_space = "go_every" outer_loop.field_space = "go_every" inner_loop.iteration_space = "go_all_pts" outer_loop.iteration_space = "go_all_pts" # Update Kernel kschedule = node.get_kernel_schedule() kernel_st = kschedule.symbol_table iteration_indices = kernel_st.iteration_indices data_arguments = kernel_st.data_arguments # Create new symbols and insert them as kernel arguments at the end of # the kernel argument list xstart_symbol = kernel_st.new_symbol( "xstart", symbol_type=DataSymbol, datatype=INTEGER_TYPE, interface=ArgumentInterface(ArgumentInterface.Access.READ)) xstop_symbol = kernel_st.new_symbol("xstop", symbol_type=DataSymbol, datatype=INTEGER_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READ)) ystart_symbol = kernel_st.new_symbol( "ystart", symbol_type=DataSymbol, datatype=INTEGER_TYPE, interface=ArgumentInterface(ArgumentInterface.Access.READ)) ystop_symbol = kernel_st.new_symbol("ystop", symbol_type=DataSymbol, datatype=INTEGER_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READ)) kernel_st.specify_argument_list( iteration_indices + data_arguments + [xstart_symbol, xstop_symbol, ystart_symbol, ystop_symbol]) # Create boundary masking conditions condition1 = BinaryOperation.create(BinaryOperation.Operator.LT, Reference(iteration_indices[0]), Reference(xstart_symbol)) condition2 = BinaryOperation.create(BinaryOperation.Operator.GT, Reference(iteration_indices[0]), Reference(xstop_symbol)) condition3 = BinaryOperation.create(BinaryOperation.Operator.LT, Reference(iteration_indices[1]), Reference(ystart_symbol)) condition4 = BinaryOperation.create(BinaryOperation.Operator.GT, Reference(iteration_indices[1]), Reference(ystop_symbol)) condition = BinaryOperation.create( BinaryOperation.Operator.OR, BinaryOperation.create(BinaryOperation.Operator.OR, condition1, condition2), BinaryOperation.create(BinaryOperation.Operator.OR, condition3, condition4)) # Insert the conditional mask as the first statement of the kernel if_statement = IfBlock.create(condition, [Return()]) kschedule.children.insert(0, if_statement) return node.root, None
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)
TMPARRAY = Array.create(ARRAY, [MY_RANGE]) # Assignments ASSIGN1 = Assignment.create(TMP1, ZERO) ASSIGN2 = Assignment.create(TMP2, ZERO) ASSIGN3 = Assignment.create(TMP2, BINARYOPERATION) ASSIGN4 = Assignment.create(TMP1, TMP2) ASSIGN5 = Assignment.create(TMP1, NARYOPERATION) ASSIGN6 = Assignment.create(TMPARRAY, TWO) # Call CALL = Call.create(ROUTINE_SYMBOL, [TMP1, BINARYOPERATION]) # If statement IF_CONDITION = BinaryOperation.create(BinaryOperation.Operator.GT, TMP1, ZERO) IFBLOCK = IfBlock.create(IF_CONDITION, [ASSIGN3, ASSIGN4]) # Loop LOOP = Loop.create(INDEX_SYMBOL, INT_ZERO, INT_ONE, INT_ONE, [IFBLOCK]) # KernelSchedule KERNEL_SCHEDULE = KernelSchedule.create( "work", SYMBOL_TABLE, [CALL, ASSIGN2, LOOP, ASSIGN5, ASSIGN6]) # Container CONTAINER_SYMBOL_TABLE = SymbolTable() CONTAINER = Container.create("CONTAINER", CONTAINER_SYMBOL_TABLE, [KERNEL_SCHEDULE]) # Import data from another container EXTERNAL_CONTAINER = ContainerSymbol("some_mod")
def create_psyir_tree(): ''' Create an example PSyIR Tree. :returns: an example PSyIR tree. :rtype: :py:class:`psyclone.psyir.nodes.Container` ''' # Symbol table, symbols and scalar datatypes symbol_table = SymbolTable() arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) symbol_table.specify_argument_list([arg1]) tmp_symbol = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_DOUBLE_TYPE) index_symbol = symbol_table.new_symbol(root_name="i", symbol_type=DataSymbol, datatype=INTEGER4_TYPE) real_kind = symbol_table.new_symbol(root_name="RKIND", symbol_type=DataSymbol, datatype=INTEGER_TYPE, constant_value=8) routine_symbol = RoutineSymbol("my_sub") # Array using precision defined by another symbol scalar_type = ScalarType(ScalarType.Intrinsic.REAL, real_kind) array = symbol_table.new_symbol(root_name="a", symbol_type=DataSymbol, datatype=ArrayType(scalar_type, [10])) # Make generators for nodes which do not have other Nodes as children, # with some predefined scalar datatypes def zero(): return Literal("0.0", REAL_TYPE) def one(): return Literal("1.0", REAL4_TYPE) def two(): return Literal("2.0", scalar_type) def int_zero(): return Literal("0", INTEGER_SINGLE_TYPE) def int_one(): return Literal("1", INTEGER8_TYPE) def tmp1(): return Reference(arg1) def tmp2(): return Reference(tmp_symbol) # Unary Operation oper = UnaryOperation.Operator.SIN unaryoperation = UnaryOperation.create(oper, tmp2()) # Binary Operation oper = BinaryOperation.Operator.ADD binaryoperation = BinaryOperation.create(oper, one(), unaryoperation) # Nary Operation oper = NaryOperation.Operator.MAX naryoperation = NaryOperation.create(oper, [tmp1(), tmp2(), one()]) # Array reference using a range lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(array), int_one()) ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(array), int_one()) my_range = Range.create(lbound, ubound) tmparray = ArrayReference.create(array, [my_range]) # Assignments assign1 = Assignment.create(tmp1(), zero()) assign2 = Assignment.create(tmp2(), zero()) assign3 = Assignment.create(tmp2(), binaryoperation) assign4 = Assignment.create(tmp1(), tmp2()) assign5 = Assignment.create(tmp1(), naryoperation) assign6 = Assignment.create(tmparray, two()) # Call call = Call.create(routine_symbol, [tmp1(), binaryoperation.copy()]) # If statement if_condition = BinaryOperation.create(BinaryOperation.Operator.GT, tmp1(), zero()) ifblock = IfBlock.create(if_condition, [assign3, assign4]) # Loop loop = Loop.create(index_symbol, int_zero(), int_one(), int_one(), [ifblock]) # KernelSchedule kernel_schedule = KernelSchedule.create( "work", symbol_table, [assign1, call, assign2, loop, assign5, assign6]) # Container container_symbol_table = SymbolTable() container = Container.create("CONTAINER", container_symbol_table, [kernel_schedule]) # Import data from another container external_container = ContainerSymbol("some_mod") container_symbol_table.add(external_container) external_var = DataSymbol("some_var", INTEGER_TYPE, interface=GlobalInterface(external_container)) container_symbol_table.add(external_var) routine_symbol.interface = GlobalInterface(external_container) container_symbol_table.add(routine_symbol) return container
def apply(self, node, options=None): '''Apply the ABS intrinsic conversion transformation to the specified node. This node must be an ABS UnaryOperation. The ABS UnaryOperation is converted to equivalent inline code. This is implemented as a PSyIR transform from: .. code-block:: python R = ... ABS(X) ... to: .. code-block:: python tmp_abs = X if tmp_abs < 0.0: res_abs = tmp_abs*-1.0 else: res_abs = tmp_abs R = ... res_abs ... where ``X`` could be an arbitrarily complex PSyIR expression and ``...`` could be arbitrary PSyIR code. 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: an ABS UnaryOperation node. :type node: :py:class:`psyclone.psyGen.UnaryOperation` :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 ABS 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). symbol_res_var = symbol_table.new_symbol("res_abs", symbol_type=DataSymbol, datatype=REAL_TYPE) symbol_tmp_var = symbol_table.new_symbol("tmp_abs", symbol_type=DataSymbol, datatype=REAL_TYPE) # Replace operation with a temporary (res_X). oper_parent.children[node.position] = Reference(symbol_res_var, parent=oper_parent) # tmp_var=X lhs = Reference(symbol_tmp_var) rhs = node.children[0] new_assignment = Assignment.create(lhs, rhs) new_assignment.parent = assignment.parent assignment.parent.children.insert(assignment.position, new_assignment) # if condition: tmp_var>0.0 lhs = Reference(symbol_tmp_var) rhs = Literal("0.0", REAL_TYPE) if_condition = BinaryOperation.create(BinaryOperation.Operator.GT, lhs, rhs) # then_body: res_var=tmp_var lhs = Reference(symbol_res_var) rhs = Reference(symbol_tmp_var) then_body = [Assignment.create(lhs, rhs)] # else_body: res_var=-1.0*tmp_var lhs = Reference(symbol_res_var) lhs_child = Reference(symbol_tmp_var) rhs_child = Literal("-1.0", REAL_TYPE) rhs = BinaryOperation.create(BinaryOperation.Operator.MUL, lhs_child, rhs_child) else_body = [Assignment.create(lhs, rhs)] # if [if_condition] then [then_body] else [else_body] if_stmt = IfBlock.create(if_condition, then_body, else_body) if_stmt.parent = assignment.parent assignment.parent.children.insert(assignment.position, if_stmt)
def create_psyir_tree(): ''' Create an example PSyIR Tree. :returns: an example PSyIR tree. :rtype: :py:class:`psyclone.psyir.nodes.Container` ''' # Symbol table, symbols and scalar datatypes symbol_table = SymbolTable() arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) symbol_table.specify_argument_list([arg1]) tmp_symbol = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_DOUBLE_TYPE) index_symbol = symbol_table.new_symbol(root_name="i", symbol_type=DataSymbol, datatype=INTEGER4_TYPE) real_kind = symbol_table.new_symbol(root_name="RKIND", symbol_type=DataSymbol, datatype=INTEGER_TYPE, constant_value=8) routine_symbol = RoutineSymbol("my_sub") # Array using precision defined by another symbol scalar_type = ScalarType(ScalarType.Intrinsic.REAL, real_kind) array = symbol_table.new_symbol(root_name="a", symbol_type=DataSymbol, datatype=ArrayType(scalar_type, [10])) # Nodes which do not have Nodes as children and (some) predefined # scalar datatypes # TODO: Issue #1136 looks at how to avoid all of the _x versions zero_1 = Literal("0.0", REAL_TYPE) zero_2 = Literal("0.0", REAL_TYPE) zero_3 = Literal("0.0", REAL_TYPE) one_1 = Literal("1.0", REAL4_TYPE) one_2 = Literal("1.0", REAL4_TYPE) one_3 = Literal("1.0", REAL4_TYPE) two = Literal("2.0", scalar_type) int_zero = Literal("0", INTEGER_SINGLE_TYPE) int_one_1 = Literal("1", INTEGER8_TYPE) int_one_2 = Literal("1", INTEGER8_TYPE) int_one_3 = Literal("1", INTEGER8_TYPE) int_one_4 = Literal("1", INTEGER8_TYPE) tmp1_1 = Reference(arg1) tmp1_2 = Reference(arg1) tmp1_3 = Reference(arg1) tmp1_4 = Reference(arg1) tmp1_5 = Reference(arg1) tmp1_6 = Reference(arg1) tmp2_1 = Reference(tmp_symbol) tmp2_2 = Reference(tmp_symbol) tmp2_3 = Reference(tmp_symbol) tmp2_4 = Reference(tmp_symbol) tmp2_5 = Reference(tmp_symbol) tmp2_6 = Reference(tmp_symbol) # Unary Operation oper = UnaryOperation.Operator.SIN unaryoperation_1 = UnaryOperation.create(oper, tmp2_1) unaryoperation_2 = UnaryOperation.create(oper, tmp2_2) # Binary Operation oper = BinaryOperation.Operator.ADD binaryoperation_1 = BinaryOperation.create(oper, one_1, unaryoperation_1) binaryoperation_2 = BinaryOperation.create(oper, one_2, unaryoperation_2) # Nary Operation oper = NaryOperation.Operator.MAX naryoperation = NaryOperation.create(oper, [tmp1_1, tmp2_3, one_3]) # Array reference using a range lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(array), int_one_1) ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(array), int_one_2) my_range = Range.create(lbound, ubound) tmparray = ArrayReference.create(array, [my_range]) # Assignments assign1 = Assignment.create(tmp1_2, zero_1) assign2 = Assignment.create(tmp2_4, zero_2) assign3 = Assignment.create(tmp2_5, binaryoperation_1) assign4 = Assignment.create(tmp1_3, tmp2_6) assign5 = Assignment.create(tmp1_4, naryoperation) assign6 = Assignment.create(tmparray, two) # Call call = Call.create(routine_symbol, [tmp1_5, binaryoperation_2]) # If statement if_condition = BinaryOperation.create(BinaryOperation.Operator.GT, tmp1_6, zero_3) ifblock = IfBlock.create(if_condition, [assign3, assign4]) # Loop loop = Loop.create(index_symbol, int_zero, int_one_3, int_one_4, [ifblock]) # KernelSchedule kernel_schedule = KernelSchedule.create( "work", symbol_table, [assign1, call, assign2, loop, assign5, assign6]) # Container container_symbol_table = SymbolTable() container = Container.create("CONTAINER", container_symbol_table, [kernel_schedule]) # Import data from another container external_container = ContainerSymbol("some_mod") container_symbol_table.add(external_container) external_var = DataSymbol("some_var", INTEGER_TYPE, interface=GlobalInterface(external_container)) container_symbol_table.add(external_var) routine_symbol.interface = GlobalInterface(external_container) container_symbol_table.add(routine_symbol) return container