def int_unary_op(op: str, c_func_name: str) -> OpDescription: return unary_op(op=op, arg_type=int_rprimitive, result_type=int_rprimitive, error_kind=ERR_NEVER, format_str='{dest} = %s{args[0]} :: int' % op, emit=call_emit(c_func_name))
def int_binary_op(op: str, c_func_name: str, result_type: RType = int_rprimitive, error_kind: int = ERR_NEVER) -> None: binary_op(op=op, arg_types=[int_rprimitive, int_rprimitive], result_type=result_type, error_kind=error_kind, format_str='{dest} = {args[0]} %s {args[1]} :: int' % op, emit=call_emit(c_func_name))
# Get the type object for 'builtins.int'. # For ordinary calls to int() we use a name_ref to the type name_ref_op('builtins.int', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('&PyLong_Type', target_type='PyObject *'), is_borrowed=True) # Convert from a float to int. We could do a bit better directly. func_op( name='builtins.int', arg_types=[float_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyLong_FromFloat'), priority=1) # int(string) func_op( name='builtins.int', arg_types=[str_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyLong_FromStr'), priority=1) # int(string, base) func_op( name='builtins.int', arg_types=[str_rprimitive, int_rprimitive],
call_negative_bool_emit, name_emit, c_custom_op, c_method_op, c_function_op) # Get the 'dict' type object. name_ref_op('builtins.dict', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('&PyDict_Type', target_type="PyObject *"), is_borrowed=True) # dict[key] dict_get_item_op = method_op(name='__getitem__', arg_types=[dict_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyDict_GetItem')) # dict[key] = value dict_set_item_op = method_op( name='__setitem__', arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive], result_type=bool_rprimitive, error_kind=ERR_FALSE, emit=call_negative_bool_emit('CPyDict_SetItem')) # key in dict binary_op( op='in', arg_types=[object_rprimitive, dict_rprimitive], result_type=bool_rprimitive, error_kind=ERR_MAGIC,
c_method_op(name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], return_type=object_rprimitive, c_function_name='CPyList_GetItemShort', error_kind=ERR_MAGIC, priority=2) # This is unsafe because it assumes that the index is a non-negative short integer # that is in-bounds for the list. list_get_item_unsafe_op = custom_op( name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], result_type=object_rprimitive, error_kind=ERR_NEVER, format_str='{dest} = {args[0]}[{args[1]}] :: unsafe list', emit=call_emit('CPyList_GetItemUnsafe')) # list[index] = obj list_set_item_op = c_method_op( name='__setitem__', arg_types=[list_rprimitive, int_rprimitive, object_rprimitive], return_type=bool_rprimitive, c_function_name='CPyList_SetItem', error_kind=ERR_FALSE, steals=[False, False, True]) # list.append(obj) list_append_op = c_method_op(name='append', arg_types=[list_rprimitive, object_rprimitive], return_type=c_int_rprimitive, c_function_name='PyList_Append',
c_method_op(name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], return_type=object_rprimitive, c_function_name='CPyList_GetItemShort', error_kind=ERR_MAGIC, priority=2) # This is unsafe because it assumes that the index is a non-negative short integer # that is in-bounds for the list. list_get_item_unsafe_op = custom_op( name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], result_type=object_rprimitive, error_kind=ERR_NEVER, format_str='{dest} = {args[0]}[{args[1]}] :: unsafe list', emit=call_emit('CPyList_GetItemUnsafe')) # list[index] = obj list_set_item_op = c_method_op( name='__setitem__', arg_types=[list_rprimitive, int_rprimitive, object_rprimitive], return_type=bool_rprimitive, c_function_name='CPyList_SetItem', error_kind=ERR_FALSE, steals=[False, False, True]) # list.append(obj) list_append_op = method_op(name='append', arg_types=[list_rprimitive, object_rprimitive], result_type=bool_rprimitive, error_kind=ERR_FALSE,
# Get the 'dict' type object. name_ref_op('builtins.dict', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=simple_emit('{dest} = (PyObject *)&PyDict_Type;'), is_borrowed=True) # dict[key] dict_get_item_op = method_op( name='__getitem__', arg_types=[dict_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyDict_GetItem')) # dict[key] = value dict_set_item_op = method_op( name='__setitem__', arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive], result_type=bool_rprimitive, error_kind=ERR_FALSE, emit=call_negative_bool_emit('CPyDict_SetItem')) # key in dict binary_op(op='in', arg_types=[object_rprimitive, dict_rprimitive], result_type=bool_rprimitive, error_kind=ERR_MAGIC, format_str='{dest} = {args[0]} in {args[1]} :: dict',
) from mypyc.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, EmitterInterface from mypyc.rtypes import object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive from typing import List new_set_op = func_op(name='builtins.set', arg_types=[], result_type=set_rprimitive, error_kind=ERR_MAGIC, emit=simple_emit('{dest} = PySet_New(NULL);')) func_op(name='builtins.set', arg_types=[object_rprimitive], result_type=set_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PySet_New')) func_op(name='builtins.frozenset', arg_types=[object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PyFrozenSet_New')) def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: temp = emitter.temp_name() emitter.emit_declaration('Py_ssize_t %s;' % temp) emitter.emit_line('%s = PySet_GET_SIZE(%s);' % (temp, args[0])) emitter.emit_line('%s = CPyTagged_ShortFromSsize_t(%s);' % (dest, temp))
'raise_exception_with_tb({args[0]}, {args[1]}, {args[2]}); {dest} = 0', emit=call_and_fail_emit('CPyErr_SetObjectAndTraceback')) # Reraise the currently raised exception. reraise_exception_op = custom_op(arg_types=[], result_type=bool_rprimitive, error_kind=ERR_FALSE, format_str='reraise_exc; {dest} = 0', emit=call_and_fail_emit('CPy_Reraise')) # Propagate exception if the CPython error indicator is set (an exception was raised). no_err_occurred_op = custom_op(arg_types=[], result_type=bool_rprimitive, error_kind=ERR_FALSE, format_str='{dest} = no_err_occurred', emit=call_emit('CPy_NoErrOccured')) # Assert that the error indicator has been set. assert_err_occured_op = custom_op( arg_types=[], result_type=void_rtype, error_kind=ERR_NEVER, format_str='assert_err_occurred', emit=simple_emit( 'assert(PyErr_Occurred() != NULL && "failure w/o err!");')) # Keep propagating a raised exception by unconditionally giving an error value. # This doesn't actually raise an exception. keep_propagating_op = custom_op(arg_types=[], result_type=bool_rprimitive, error_kind=ERR_FALSE,
# Construct a list from values: [item1, item2, ....] new_list_op = custom_op(arg_types=[object_rprimitive], result_type=list_rprimitive, is_var_arg=True, error_kind=ERR_MAGIC, steals=True, format_str='{dest} = [{comma_args}]', emit=emit_new) # list[index] (for an integer index) list_get_item_op = method_op(name='__getitem__', arg_types=[list_rprimitive, int_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyList_GetItem')) # Version with no int bounds check for when it is known to be short method_op(name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyList_GetItemShort'), priority=2) # This is unsafe because it assumes that the index is a non-negative short integer # that is in-bounds for the list. list_get_item_unsafe_op = custom_op( name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], result_type=object_rprimitive,
call_negative_bool_emit, ) # Get the 'builtins.list' type object. name_ref_op('builtins.list', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('&PyList_Type', target_type='PyObject *'), is_borrowed=True) # list(obj) to_list = func_op(name='builtins.list', arg_types=[object_rprimitive], result_type=list_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PySequence_List')) def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: # TODO: This would be better split into multiple smaller ops. emitter.emit_line('%s = PyList_New(%d); ' % (dest, len(args))) emitter.emit_line('if (likely(%s != NULL)) {' % dest) for i, arg in enumerate(args): emitter.emit_line('PyList_SET_ITEM(%s, %s, %s);' % (dest, i, arg)) emitter.emit_line('}') # Construct a list from values: [item1, item2, ....] new_list_op = custom_op(arg_types=[object_rprimitive], result_type=list_rprimitive, is_var_arg=True,
# str.join(obj) method_op( name='join', arg_types=[str_rprimitive, object_rprimitive], result_type=str_rprimitive, error_kind=ERR_MAGIC, emit=simple_emit('{dest} = PyUnicode_Join({args[0]}, {args[1]});')) # str[index] (for an int index) method_op( name='__getitem__', arg_types=[str_rprimitive, int_rprimitive], result_type=str_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyStr_GetItem')) # str.split(...) str_split_types = [str_rprimitive, str_rprimitive, int_rprimitive] # type: List[RType] str_split_emits = [simple_emit('{dest} = PyUnicode_Split({args[0]}, NULL, -1);'), simple_emit('{dest} = PyUnicode_Split({args[0]}, {args[1]}, -1);'), simple_emit('{dest} = CPyStr_Split({args[0]}, {args[1]}, {args[2]});')] \ # type: List[EmitCallback] for i in range(len(str_split_types)): method_op( name='split', arg_types=str_split_types[0:i+1], result_type=list_rprimitive, error_kind=ERR_MAGIC, emit=str_split_emits[i])
from typing import List from mypyc.ir.ops import ( EmitterInterface, ERR_NEVER, ERR_MAGIC ) from mypyc.ir.rtypes import tuple_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive from mypyc.primitives.registry import func_op, method_op, custom_op, call_emit, simple_emit tuple_get_item_op = method_op( name='__getitem__', arg_types=[tuple_rprimitive, int_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPySequenceTuple_GetItem')) new_tuple_op = custom_op( arg_types=[object_rprimitive], result_type=tuple_rprimitive, is_var_arg=True, error_kind=ERR_MAGIC, steals=False, format_str='{dest} = ({comma_args}) :: tuple', emit=simple_emit('{dest} = PyTuple_Pack({num_args}{pre_comma_args});')) def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: temp = emitter.temp_name() emitter.emit_declaration('Py_ssize_t %s;' % temp)
simple_emit, call_emit, call_negative_bool_emit, ) name_ref_op('builtins.list', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=simple_emit('{dest} = (PyObject *)&PyList_Type;'), is_borrowed=True) to_list = func_op(name='builtins.list', arg_types=[object_rprimitive], result_type=list_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PySequence_List')) def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: # TODO: This would be better split into multiple smaller ops. emitter.emit_line('%s = PyList_New(%d); ' % (dest, len(args))) emitter.emit_line('if (likely(%s != NULL)) {' % dest) for i, arg in enumerate(args): emitter.emit_line('PyList_SET_ITEM(%s, %s, %s);' % (dest, i, arg)) emitter.emit_line('}') new_list_op = custom_op(arg_types=[object_rprimitive], result_type=list_rprimitive, is_var_arg=True, error_kind=ERR_MAGIC,
# I guess unboxing ourselves would save a check and branch though? # Get the type object for 'builtins.int'. # For ordinary calls to int() we use a name_ref to the type name_ref_op('builtins.int', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=simple_emit('{dest} = (PyObject *)&PyLong_Type;'), is_borrowed=True) # Convert from a float to int. We could do a bit better directly. func_op(name='builtins.int', arg_types=[float_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyLong_FromFloat'), priority=1) # int(string) func_op(name='builtins.int', arg_types=[str_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyLong_FromStr'), priority=1) # int(string, base) func_op(name='builtins.int', arg_types=[str_rprimitive, int_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC,
arg_types=[object_rprimitive], result_type=object_rprimitive, is_var_arg=True, error_kind=ERR_MAGIC, format_str='{dest} = py_call({comma_args})', emit=simple_emit( '{dest} = PyObject_CallFunctionObjArgs({comma_args}, NULL);')) # Call callable object with positional + keyword args: func(*args, **kwargs) # Arguments are (func, *args tuple, **kwargs dict). py_call_with_kwargs_op = custom_op( arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, format_str='{dest} = py_call_with_kwargs({args[0]}, {args[1]}, {args[2]})', emit=call_emit('PyObject_Call')) # Call method with positional arguments: obj.method(arg1, ...) # Arguments are (object, attribute name, arg1, ...). py_method_call_op = custom_op( arg_types=[object_rprimitive], result_type=object_rprimitive, is_var_arg=True, error_kind=ERR_MAGIC, format_str='{dest} = py_method_call({comma_args})', emit=simple_emit( '{dest} = CPyObject_CallMethodObjArgs({comma_args}, NULL);')) # len(obj) generic_len_op = c_custom_op(arg_types=[object_rprimitive], return_type=int_rprimitive,
name_ref_op, method_op, call_emit, name_emit, c_method_op) # Get the 'str' type object. name_ref_op('builtins.str', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('&PyUnicode_Type', target_type='PyObject *'), is_borrowed=True) # str(obj) func_op(name='builtins.str', arg_types=[object_rprimitive], result_type=str_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PyObject_Str')) # str1 + str2 binary_op(op='+', arg_types=[str_rprimitive, str_rprimitive], result_type=str_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PyUnicode_Concat')) # str.join(obj) c_method_op(name='join', arg_types=[str_rprimitive, object_rprimitive], return_type=str_rprimitive, c_function_name='PyUnicode_Join', error_kind=ERR_MAGIC)
'{dest} = (PyObject*) _PyType_CalculateMetaclass((PyTypeObject *){args[0]}, {args[1]});' ), is_borrowed=True) # Import a module import_op = c_custom_op(arg_types=[str_rprimitive], return_type=object_rprimitive, c_function_name='PyImport_Import', error_kind=ERR_MAGIC) # Get the sys.modules dictionary get_module_dict_op = custom_op(name='get_module_dict', arg_types=[], result_type=dict_rprimitive, error_kind=ERR_NEVER, emit=call_emit('PyImport_GetModuleDict'), is_borrowed=True) # isinstance(obj, cls) c_function_op(name='builtins.isinstance', arg_types=[object_rprimitive, object_rprimitive], return_type=c_int_rprimitive, c_function_name='PyObject_IsInstance', error_kind=ERR_NEG_INT, truncated_type=bool_rprimitive) # Faster isinstance(obj, cls) that only works with native classes and doesn't perform # type checking of the type argument. fast_isinstance_op = func_op( 'builtins.isinstance', arg_types=[object_rprimitive, object_rprimitive],
emit=name_emit('Py_NotImplemented'), is_borrowed=True) # id(obj) c_function_op(name='builtins.id', arg_types=[object_rprimitive], return_type=int_rprimitive, c_function_name='CPyTagged_Id', error_kind=ERR_NEVER) # Return the result of obj.__await()__ or obj.__iter__() (if no __await__ exists) coro_op = custom_op(name='get_coroutine_obj', arg_types=[object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPy_GetCoro')) # Do obj.send(value), or a next(obj) if second arg is None. # (This behavior is to match the PEP 380 spec for yield from.) # Like next_raw_op, don't swallow StopIteration, # but also don't propagate an error. # Can return NULL: see next_op. send_op = custom_op(name='send', arg_types=[object_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_NEVER, emit=call_emit('CPyIter_Send')) # This is sort of unfortunate but oh well: yield_from_except performs most of the # error handling logic in `yield from` operations. It returns a bool and a value. # If the bool is true, then a StopIteration was received and we should return.
result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('Py_Ellipsis'), is_borrowed=True) not_implemented_op = name_ref_op(name='builtins.NotImplemented', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('Py_NotImplemented'), is_borrowed=True) func_op(name='builtins.id', arg_types=[object_rprimitive], result_type=int_rprimitive, error_kind=ERR_NEVER, emit=call_emit('CPyTagged_Id')) iter_op = func_op(name='builtins.iter', arg_types=[object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('PyObject_GetIter')) coro_op = custom_op(name='get_coroutine_obj', arg_types=[object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPy_GetCoro')) # Although the error_kind is set to be ERR_NEVER, this can actually # return NULL, and thus it must be checked using Branch.IS_ERROR.
# These int constructors produce object_rprimitives that then need to be unboxed # I guess unboxing ourselves would save a check and branch though? # For ordinary calls to int() we use a name_ref to the type name_ref_op('builtins.int', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=simple_emit('{dest} = (PyObject *)&PyLong_Type;'), is_borrowed=True) # Convert from a float. We could do a bit better directly. func_op(name='builtins.int', arg_types=[float_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyLong_FromFloat'), priority=1) def int_binary_op(op: str, c_func_name: str, result_type: RType = int_rprimitive, error_kind: int = ERR_NEVER) -> None: binary_op(op=op, arg_types=[int_rprimitive, int_rprimitive], result_type=result_type, error_kind=error_kind, format_str='{dest} = {args[0]} %s {args[1]} :: int' % op, emit=call_emit(c_func_name))
for op, funcname in [('+', 'PyNumber_Add'), ('-', 'PyNumber_Subtract'), ('*', 'PyNumber_Multiply'), ('//', 'PyNumber_FloorDivide'), ('/', 'PyNumber_TrueDivide'), ('%', 'PyNumber_Remainder'), ('<<', 'PyNumber_Lshift'), ('>>', 'PyNumber_Rshift'), ('&', 'PyNumber_And'), ('^', 'PyNumber_Xor'), ('|', 'PyNumber_Or')]: binary_op(op=op, arg_types=[object_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit(funcname), priority=0) for op, funcname in [('+=', 'PyNumber_InPlaceAdd'), ('-=', 'PyNumber_InPlaceSubtract'), ('*=', 'PyNumber_InPlaceMultiply'), ('@=', 'PyNumber_InPlaceMatrixMultiply'), ('//=', 'PyNumber_InPlaceFloorDivide'), ('/=', 'PyNumber_InPlaceTrueDivide'), ('%=', 'PyNumber_InPlaceRemainder'), ('<<=', 'PyNumber_InPlaceLshift'), ('>>=', 'PyNumber_InPlaceRshift'), ('&=', 'PyNumber_InPlaceAnd'), ('^=', 'PyNumber_InPlaceXor'), ('|=', 'PyNumber_InPlaceOr')]: binary_op(op=op,
c_function_name='PyUnicode_Join', error_kind=ERR_MAGIC) # str[index] (for an int index) c_method_op(name='__getitem__', arg_types=[str_rprimitive, int_rprimitive], return_type=str_rprimitive, c_function_name='CPyStr_GetItem', error_kind=ERR_MAGIC) # str.split(...) str_split_types = [str_rprimitive, str_rprimitive, int_rprimitive] # type: List[RType] str_split_emits = [simple_emit('{dest} = PyUnicode_Split({args[0]}, NULL, -1);'), simple_emit('{dest} = PyUnicode_Split({args[0]}, {args[1]}, -1);'), call_emit('CPyStr_Split')] \ # type: List[EmitCallback] for i in range(len(str_split_types)): method_op(name='split', arg_types=str_split_types[0:i + 1], result_type=list_rprimitive, error_kind=ERR_MAGIC, emit=str_split_emits[i]) # str1 += str2 # # PyUnicodeAppend makes an effort to reuse the LHS when the refcount # is 1. This is super dodgy but oh well, the interpreter does it. c_binary_op(name='+=', arg_types=[str_rprimitive, str_rprimitive],
name_emit, ) # Get the 'dict' type object. name_ref_op('builtins.dict', result_type=object_rprimitive, error_kind=ERR_NEVER, emit=name_emit('&PyDict_Type', target_type="PyObject *"), is_borrowed=True) # dict[key] dict_get_item_op = method_op(name='__getitem__', arg_types=[dict_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, emit=call_emit('CPyDict_GetItem')) # dict[key] = value dict_set_item_op = method_op( name='__setitem__', arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive], result_type=bool_rprimitive, error_kind=ERR_FALSE, emit=call_negative_bool_emit('CPyDict_SetItem')) # key in dict binary_op( op='in', arg_types=[object_rprimitive, dict_rprimitive], result_type=bool_rprimitive, error_kind=ERR_MAGIC,