1# Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com 2# 3# Part of "Nuitka", an optimizing Python compiler that is compatible and 4# integrates with CPython, but also works on its own. 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18""" Codes for operations. 19 20There are unary and binary operations. Many of them have specializations and 21of course types could play into it. Then there is also the added difficulty of 22in-place assignments, which have other operation variants. 23""" 24 25from . import HelperDefinitions, OperatorCodes 26from .CodeHelpers import ( 27 generateChildExpressionsCode, 28 pickCodeHelper, 29 withObjectCodeTemporaryAssignment, 30) 31from .ErrorCodes import ( 32 getErrorExitBoolCode, 33 getErrorExitCode, 34 getTakeReferenceCode, 35) 36 37 38def generateOperationBinaryCode(to_name, expression, emit, context): 39 left_arg_name, right_arg_name = generateChildExpressionsCode( 40 expression=expression, emit=emit, context=context 41 ) 42 43 # TODO: Decide and use one single spelling, inplace or in_place 44 inplace = expression.isInplaceSuspect() 45 46 _getBinaryOperationCode( 47 to_name=to_name, 48 expression=expression, 49 operator=expression.getOperator(), 50 arg_names=(left_arg_name, right_arg_name), 51 in_place=inplace, 52 emit=emit, 53 context=context, 54 ) 55 56 57def generateOperationNotCode(to_name, expression, emit, context): 58 (arg_name,) = generateChildExpressionsCode( 59 expression=expression, emit=emit, context=context 60 ) 61 62 res_name = context.getIntResName() 63 64 emit("%s = CHECK_IF_TRUE(%s);" % (res_name, arg_name)) 65 66 getErrorExitBoolCode( 67 condition="%s == -1" % res_name, 68 release_name=arg_name, 69 needs_check=expression.subnode_operand.mayRaiseExceptionBool(BaseException), 70 emit=emit, 71 context=context, 72 ) 73 74 to_name.getCType().emitAssignmentCodeFromBoolCondition( 75 to_name=to_name, condition="%s == 0" % res_name, emit=emit 76 ) 77 78 79def generateOperationUnaryCode(to_name, expression, emit, context): 80 (arg_name,) = generateChildExpressionsCode( 81 expression=expression, emit=emit, context=context 82 ) 83 84 _getUnaryOperationCode( 85 to_name=to_name, 86 expression=expression, 87 operator=expression.getOperator(), 88 arg_name=arg_name, 89 needs_check=expression.mayRaiseException(BaseException), 90 emit=emit, 91 context=context, 92 ) 93 94 95def _getBinaryOperationCode( 96 to_name, expression, operator, arg_names, in_place, emit, context 97): 98 left = expression.subnode_left 99 100 ref_count = 1 101 needs_check = expression.mayRaiseExceptionOperation() 102 103 helper = pickCodeHelper( 104 prefix="BINARY_OPERATION_%s" 105 % HelperDefinitions.getCodeNameForOperation(operator), 106 suffix="INPLACE" if operator[0] == "I" else "", 107 target_type=None if operator[0] == "I" else to_name.getCType(), 108 left_shape=left.getTypeShape(), 109 right_shape=expression.subnode_right.getTypeShape(), 110 helpers=HelperDefinitions.getSpecializedOperations(operator), 111 nonhelpers=HelperDefinitions.getNonSpecializedOperations(operator), 112 source_ref=expression.source_ref, 113 ) 114 115 # We must assume to write to a variable is "in_place" is active, not e.g. 116 # a constant reference. That was asserted before calling us. 117 if in_place or "INPLACE" in helper.helper_name: 118 res_name = context.getBoolResName() 119 120 # For module variable C type to reference later. 121 if left.isExpressionVariableRef() and left.getVariable().isModuleVariable(): 122 emit("%s = %s;" % (context.getInplaceLeftName(), arg_names[0])) 123 124 if ( 125 not left.isExpressionVariableRef() 126 and not left.isExpressionTempVariableRef() 127 ): 128 if not context.needsCleanup(arg_names[0]): 129 getTakeReferenceCode(arg_names[0], emit) 130 131 emit("%s = %s(&%s, %s);" % (res_name, helper, arg_names[0], arg_names[1])) 132 133 getErrorExitBoolCode( 134 condition="%s == false" % res_name, 135 release_names=arg_names, 136 needs_check=needs_check, 137 emit=emit, 138 context=context, 139 ) 140 141 emit("%s = %s;" % (to_name, arg_names[0])) 142 143 if ( 144 not left.isExpressionVariableRef() 145 and not left.isExpressionTempVariableRef() 146 ): 147 context.addCleanupTempName(to_name) 148 else: 149 helper.emitHelperCall( 150 to_name=to_name, 151 arg_names=arg_names, 152 ref_count=ref_count, 153 needs_check=needs_check, 154 emit=emit, 155 context=context, 156 ) 157 158 159def _getUnaryOperationCode( 160 to_name, expression, operator, arg_name, needs_check, emit, context 161): 162 impl_helper, ref_count = OperatorCodes.unary_operator_codes[operator] 163 164 helper = "UNARY_OPERATION" 165 prefix_args = (impl_helper,) 166 167 with withObjectCodeTemporaryAssignment( 168 to_name, "op_%s_res" % operator.lower(), expression, emit, context 169 ) as value_name: 170 171 emit( 172 "%s = %s(%s);" 173 % ( 174 value_name, 175 helper, 176 ", ".join(str(arg_name) for arg_name in prefix_args + (arg_name,)), 177 ) 178 ) 179 180 getErrorExitCode( 181 check_name=value_name, 182 release_name=arg_name, 183 needs_check=needs_check, 184 emit=emit, 185 context=context, 186 ) 187 188 if ref_count: 189 context.addCleanupTempName(value_name) 190