def test_unaryoperation_node_str(): ''' Check the view method of the UnaryOperation class.''' ref1 = Reference(DataSymbol("a", REAL_SINGLE_TYPE)) unary_operation = UnaryOperation.create(UnaryOperation.Operator.MINUS, ref1) coloredtext = colored("UnaryOperation", UnaryOperation._colour) assert coloredtext+"[operator:'MINUS']" in unary_operation.node_str()
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
def example_psyir(create_expression): '''Utility function that creates a PSyIR tree containing an ABS intrinsic operator and returns the operator. :param function create_expresssion: function used to create the \ content of the ABS operator. :returns: PSyIR ABS operator instance. :rtype: :py:class:`psyclone.psyGen.UnaryOperation` ''' symbol_table = SymbolTable() arg1 = symbol_table.new_symbol("arg", symbol_type=DataSymbol, datatype=REAL_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) local = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE) symbol_table.specify_argument_list([arg1]) var1 = Reference(arg1) var2 = Reference(local) oper = UnaryOperation.Operator.ABS operation = UnaryOperation.create(oper, create_expression(var1)) assign = Assignment.create(var2, operation) _ = KernelSchedule.create("abs_example", symbol_table, [assign]) return operation
def test_cw_array(): '''Check the CWriter class array method correctly prints out the C representation of an array. ''' cwriter = CWriter() symbol = DataSymbol('a', REAL_TYPE) arr = ArrayReference(symbol) lit = Literal('0.0', REAL_TYPE) assignment = Assignment.create(arr, lit) # An array without any children (dimensions) should produce an error. with pytest.raises(VisitorError) as excinfo: result = cwriter(assignment) assert "Arrays must have at least 1 dimension but found node: '" \ in str(excinfo.value) # Dimensions can be references, literals or operations arr.addchild(Reference(DataSymbol('b', INTEGER_TYPE), parent=arr)) arr.addchild(Literal('1', INTEGER_TYPE, parent=arr)) uop = UnaryOperation.create(UnaryOperation.Operator.MINUS, Literal('2', INTEGER_TYPE)) uop.parent = arr arr.addchild(uop) result = cwriter(assignment) # Results is reversed and flatten (row-major 1D) # dimensions are called <name>LEN<dimension> by convention assert result == "a[(-2) * aLEN2 * aLEN1 + 1 * aLEN1 + b] = 0.0;\n"
def test_validate(): '''Check that the validate method raises exceptions as expected.''' dummy = DummyTrans() # operator_name, classes and operators are usually set by the # Transformation's __init__ method but set them manually here to # avoid creating multiple implementations of DummyTrans. dummy._operator_name = "hello" dummy._classes = (UnaryOperation, ) dummy._operators = (UnaryOperation.Operator.ABS, ) var = Literal("0.0", REAL_TYPE) operator = UnaryOperation.create(UnaryOperation.Operator.ABS, var) with pytest.raises(TransformationError) as excinfo: dummy.validate(operator) assert ("This transformation requires the operator to be part of an " "assignment statement, but no such assignment was found." in str(excinfo.value)) reference = Reference(DataSymbol("fred", REAL_TYPE)) _ = Assignment.create(lhs=reference, rhs=operator) with pytest.raises(TransformationError) as excinfo: dummy.validate(None) assert ("The supplied node argument is not a hello operator, found " "'NoneType'." in str(excinfo.value)) with pytest.raises(TransformationError) as excinfo: dummy.validate(UnaryOperation(UnaryOperation.Operator.SIN, var)) assert ("Error in Hello2CodeTrans transformation. The supplied node " "operator is invalid, found 'Operator.SIN'." in str(excinfo.value)) dummy.validate(operator)
def test_unaryoperation_node_str(): ''' Check the view method of the UnaryOperation class.''' from psyclone.psyir.nodes.node import colored, SCHEDULE_COLOUR_MAP ref1 = Reference(DataSymbol("a", REAL_SINGLE_TYPE)) unary_operation = UnaryOperation.create(UnaryOperation.Operator.MINUS, ref1) coloredtext = colored("UnaryOperation", SCHEDULE_COLOUR_MAP["Operation"]) assert coloredtext + "[operator:'MINUS']" in unary_operation.node_str()
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
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
def test_unaryoperation_create(): '''Test that the create method in the UnaryOperation class correctly creates a UnaryOperation instance. ''' child = Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)) oper = UnaryOperation.Operator.SIN unaryoperation = UnaryOperation.create(oper, child) check_links(unaryoperation, [child]) result = FortranWriter().unaryoperation_node(unaryoperation) assert result == "SIN(tmp)"
def test_unaryoperation_create_invalid(): '''Test that the create method in a UnaryOperation class raises the expected exception if the provided input is invalid. ''' # oper not a UnaryOperator.Operator. with pytest.raises(GenerationError) as excinfo: _ = UnaryOperation.create( "invalid", Reference(DataSymbol("tmp", REAL_SINGLE_TYPE))) assert ("oper argument in create method of UnaryOperation class should " "be a PSyIR UnaryOperation Operator but found 'str'." 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.psyir.nodes.Routine` :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.psyGen.InvokeSchedule`, \ :py:class:`psyclone.undoredo.Memento`) ''' routine = node self.validate(routine, options) def is_conditional_return(node): ''' :param node: node to evaluate. :type node: :py:class:`psyclone.psyir.nodes.Node` :returns: whether the given node represents a conditional return \ expression. ''' if not isinstance(node, IfBlock): return False if node.else_body is not None: return False return isinstance(node.if_body[0], Return) for statement in routine[:]: if is_conditional_return(statement): # Reverse condition adding a NOT operator new_condition = UnaryOperation.create( UnaryOperation.Operator.NOT, statement.condition) statement.children[0] = new_condition new_condition.parent = statement # Remove return statement (and any dead code inside the loop) statement.if_body.children = [] # Then move any remaining statement after the conditional # statement inside the loop body while len(statement.parent.children) > statement.position + 1: move = statement.parent.children.pop() statement.if_body.children.insert(0, move) move.parent = statement.if_body
def test_is_array_range(): '''test that the is_array_range method behaves as expected, returning true if the LHS of the assignment is an array range access. ''' one = Literal("1.0", REAL_TYPE) int_one = Literal("1", INTEGER_TYPE) int_ten = Literal("10", INTEGER_TYPE) # lhs is an array reference with a range array_type = ArrayType(REAL_TYPE, [10, 10]) symbol = DataSymbol("x", array_type) x_range = Range.create(int_one, int_ten.copy(), int_one.copy()) array_ref = ArrayReference.create(symbol, [x_range, int_one.copy()]) assignment = Assignment.create(array_ref, one.copy()) assert assignment.is_array_range is True # Check when lhs consists of various forms of structure access 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) # Create the definition of the 'field_type', contains array of grid_types field_type_def = StructureType.create([ ("data", ArrayType(REAL_SINGLE_TYPE, [10]), Symbol.Visibility.PUBLIC), ("sub_meshes", ArrayType(grid_type_symbol, [3]), Symbol.Visibility.PUBLIC) ]) field_type_symbol = DataTypeSymbol("field_type", field_type_def) field_symbol = DataSymbol("wind", field_type_symbol) # Array reference to component of derived type using a range lbound = BinaryOperation.create( BinaryOperation.Operator.LBOUND, StructureReference.create(field_symbol, ["data"]), int_one.copy()) ubound = BinaryOperation.create( BinaryOperation.Operator.UBOUND, StructureReference.create(field_symbol, ["data"]), int_one.copy()) my_range = Range.create(lbound, ubound) data_ref = StructureReference.create(field_symbol, [("data", [my_range])]) assign = Assignment.create(data_ref, one.copy()) assert assign.is_array_range is True # Access to slice of 'sub_meshes': wind%sub_meshes(1:3)%dx = 1.0 sub_range = Range.create(int_one.copy(), Literal("3", INTEGER_TYPE)) dx_ref = StructureReference.create(field_symbol, [("sub_meshes", [sub_range]), "dx"]) sub_assign = Assignment.create(dx_ref, one.copy()) assert sub_assign.is_array_range is True # Create an array of these derived types and assign to a slice: # chi(1:10)%data(1) = 1.0 field_bundle_symbol = DataSymbol("chi", ArrayType(field_type_symbol, [3])) fld_range = Range.create(int_one.copy(), Literal("10", INTEGER_TYPE)) fld_ref = ArrayOfStructuresReference.create(field_bundle_symbol, [fld_range], [("data", [int_one.copy()])]) fld_assign = Assignment.create(fld_ref, one.copy()) assert fld_assign.is_array_range is True # When the slice has two operator ancestors, none of which are a reduction # e.g y(1, INT(ABS(map(:, 1)))) = 1.0 int_array_type = ArrayType(INTEGER_SINGLE_TYPE, [10, 10]) map_sym = DataSymbol("map", int_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) abs_op = UnaryOperation.create( UnaryOperation.Operator.ABS, ArrayReference.create(map_sym, [my_range1, int_one.copy()])) int_op = UnaryOperation.create(UnaryOperation.Operator.INT, abs_op) assignment = Assignment.create( ArrayReference.create(symbol, [int_one.copy(), int_op]), one.copy()) assert assignment.is_array_range is True
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 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
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)
ARRAY = DataSymbol(ARRAY_NAME, ArrayType(SCALAR_TYPE, [10])) SYMBOL_TABLE.add(ARRAY) # Nodes which do not have Nodes as children and (some) predefined # scalar datatypes ZERO = Literal("0.0", REAL_TYPE) ONE = Literal("1.0", REAL4_TYPE) TWO = Literal("2.0", SCALAR_TYPE) INT_ZERO = Literal("0", INTEGER_SINGLE_TYPE) INT_ONE = Literal("1", INTEGER8_TYPE) TMP1 = Reference(ARG1) TMP2 = 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)