generic_ops.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. """Fallback primitive operations that operate on 'object' operands.
  2. These just call the relevant Python C API function or a thin wrapper
  3. around an API function. Most of these also have faster, specialized
  4. ops that operate on some more specific types.
  5. Many of these ops are given a low priority (0) so that specialized ops
  6. will take precedence. If your specialized op doesn't seem to be used,
  7. check that the priorities are configured properly.
  8. """
  9. from __future__ import annotations
  10. from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER
  11. from mypyc.ir.rtypes import (
  12. bool_rprimitive,
  13. c_int_rprimitive,
  14. c_pyssize_t_rprimitive,
  15. c_size_t_rprimitive,
  16. int_rprimitive,
  17. object_pointer_rprimitive,
  18. object_rprimitive,
  19. pointer_rprimitive,
  20. )
  21. from mypyc.primitives.registry import (
  22. ERR_NEG_INT,
  23. binary_op,
  24. custom_op,
  25. function_op,
  26. method_op,
  27. unary_op,
  28. )
  29. # Binary operations
  30. for op, opid in [
  31. ("==", 2), # PY_EQ
  32. ("!=", 3), # PY_NE
  33. ("<", 0), # PY_LT
  34. ("<=", 1), # PY_LE
  35. (">", 4), # PY_GT
  36. (">=", 5),
  37. ]: # PY_GE
  38. # The result type is 'object' since that's what PyObject_RichCompare returns.
  39. binary_op(
  40. name=op,
  41. arg_types=[object_rprimitive, object_rprimitive],
  42. return_type=object_rprimitive,
  43. c_function_name="PyObject_RichCompare",
  44. error_kind=ERR_MAGIC,
  45. extra_int_constants=[(opid, c_int_rprimitive)],
  46. priority=0,
  47. )
  48. for op, funcname in [
  49. ("+", "PyNumber_Add"),
  50. ("-", "PyNumber_Subtract"),
  51. ("*", "PyNumber_Multiply"),
  52. ("//", "PyNumber_FloorDivide"),
  53. ("/", "PyNumber_TrueDivide"),
  54. ("%", "PyNumber_Remainder"),
  55. ("<<", "PyNumber_Lshift"),
  56. (">>", "PyNumber_Rshift"),
  57. ("&", "PyNumber_And"),
  58. ("^", "PyNumber_Xor"),
  59. ("|", "PyNumber_Or"),
  60. ("@", "PyNumber_MatrixMultiply"),
  61. ]:
  62. binary_op(
  63. name=op,
  64. arg_types=[object_rprimitive, object_rprimitive],
  65. return_type=object_rprimitive,
  66. c_function_name=funcname,
  67. error_kind=ERR_MAGIC,
  68. priority=0,
  69. )
  70. function_op(
  71. name="builtins.divmod",
  72. arg_types=[object_rprimitive, object_rprimitive],
  73. return_type=object_rprimitive,
  74. c_function_name="PyNumber_Divmod",
  75. error_kind=ERR_MAGIC,
  76. priority=0,
  77. )
  78. for op, funcname in [
  79. ("+=", "PyNumber_InPlaceAdd"),
  80. ("-=", "PyNumber_InPlaceSubtract"),
  81. ("*=", "PyNumber_InPlaceMultiply"),
  82. ("@=", "PyNumber_InPlaceMatrixMultiply"),
  83. ("//=", "PyNumber_InPlaceFloorDivide"),
  84. ("/=", "PyNumber_InPlaceTrueDivide"),
  85. ("%=", "PyNumber_InPlaceRemainder"),
  86. ("<<=", "PyNumber_InPlaceLshift"),
  87. (">>=", "PyNumber_InPlaceRshift"),
  88. ("&=", "PyNumber_InPlaceAnd"),
  89. ("^=", "PyNumber_InPlaceXor"),
  90. ("|=", "PyNumber_InPlaceOr"),
  91. ]:
  92. binary_op(
  93. name=op,
  94. arg_types=[object_rprimitive, object_rprimitive],
  95. return_type=object_rprimitive,
  96. c_function_name=funcname,
  97. error_kind=ERR_MAGIC,
  98. priority=0,
  99. )
  100. for op, c_function in (("**", "CPyNumber_Power"), ("**=", "CPyNumber_InPlacePower")):
  101. binary_op(
  102. name=op,
  103. arg_types=[object_rprimitive, object_rprimitive],
  104. return_type=object_rprimitive,
  105. error_kind=ERR_MAGIC,
  106. c_function_name=c_function,
  107. priority=0,
  108. )
  109. for arg_count, c_function in ((2, "CPyNumber_Power"), (3, "PyNumber_Power")):
  110. function_op(
  111. name="builtins.pow",
  112. arg_types=[object_rprimitive] * arg_count,
  113. return_type=object_rprimitive,
  114. error_kind=ERR_MAGIC,
  115. c_function_name=c_function,
  116. priority=0,
  117. )
  118. binary_op(
  119. name="in",
  120. arg_types=[object_rprimitive, object_rprimitive],
  121. return_type=c_int_rprimitive,
  122. c_function_name="PySequence_Contains",
  123. error_kind=ERR_NEG_INT,
  124. truncated_type=bool_rprimitive,
  125. ordering=[1, 0],
  126. priority=0,
  127. )
  128. # Unary operations
  129. for op, funcname in [
  130. ("-", "PyNumber_Negative"),
  131. ("+", "PyNumber_Positive"),
  132. ("~", "PyNumber_Invert"),
  133. ]:
  134. unary_op(
  135. name=op,
  136. arg_type=object_rprimitive,
  137. return_type=object_rprimitive,
  138. c_function_name=funcname,
  139. error_kind=ERR_MAGIC,
  140. priority=0,
  141. )
  142. unary_op(
  143. name="not",
  144. arg_type=object_rprimitive,
  145. return_type=c_int_rprimitive,
  146. c_function_name="PyObject_Not",
  147. error_kind=ERR_NEG_INT,
  148. truncated_type=bool_rprimitive,
  149. priority=0,
  150. )
  151. # abs(obj)
  152. function_op(
  153. name="builtins.abs",
  154. arg_types=[object_rprimitive],
  155. return_type=object_rprimitive,
  156. c_function_name="PyNumber_Absolute",
  157. error_kind=ERR_MAGIC,
  158. priority=0,
  159. )
  160. # obj1[obj2]
  161. method_op(
  162. name="__getitem__",
  163. arg_types=[object_rprimitive, object_rprimitive],
  164. return_type=object_rprimitive,
  165. c_function_name="PyObject_GetItem",
  166. error_kind=ERR_MAGIC,
  167. priority=0,
  168. )
  169. # obj1[obj2] = obj3
  170. method_op(
  171. name="__setitem__",
  172. arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
  173. return_type=c_int_rprimitive,
  174. c_function_name="PyObject_SetItem",
  175. error_kind=ERR_NEG_INT,
  176. priority=0,
  177. )
  178. # del obj1[obj2]
  179. method_op(
  180. name="__delitem__",
  181. arg_types=[object_rprimitive, object_rprimitive],
  182. return_type=c_int_rprimitive,
  183. c_function_name="PyObject_DelItem",
  184. error_kind=ERR_NEG_INT,
  185. priority=0,
  186. )
  187. # hash(obj)
  188. function_op(
  189. name="builtins.hash",
  190. arg_types=[object_rprimitive],
  191. return_type=int_rprimitive,
  192. c_function_name="CPyObject_Hash",
  193. error_kind=ERR_MAGIC,
  194. )
  195. # getattr(obj, attr)
  196. py_getattr_op = function_op(
  197. name="builtins.getattr",
  198. arg_types=[object_rprimitive, object_rprimitive],
  199. return_type=object_rprimitive,
  200. c_function_name="CPyObject_GetAttr",
  201. error_kind=ERR_MAGIC,
  202. )
  203. # getattr(obj, attr, default)
  204. function_op(
  205. name="builtins.getattr",
  206. arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
  207. return_type=object_rprimitive,
  208. c_function_name="CPyObject_GetAttr3",
  209. error_kind=ERR_MAGIC,
  210. )
  211. # setattr(obj, attr, value)
  212. py_setattr_op = function_op(
  213. name="builtins.setattr",
  214. arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
  215. return_type=c_int_rprimitive,
  216. c_function_name="PyObject_SetAttr",
  217. error_kind=ERR_NEG_INT,
  218. )
  219. # hasattr(obj, attr)
  220. py_hasattr_op = function_op(
  221. name="builtins.hasattr",
  222. arg_types=[object_rprimitive, object_rprimitive],
  223. return_type=bool_rprimitive,
  224. c_function_name="PyObject_HasAttr",
  225. error_kind=ERR_NEVER,
  226. )
  227. # del obj.attr
  228. py_delattr_op = function_op(
  229. name="builtins.delattr",
  230. arg_types=[object_rprimitive, object_rprimitive],
  231. return_type=c_int_rprimitive,
  232. c_function_name="PyObject_DelAttr",
  233. error_kind=ERR_NEG_INT,
  234. )
  235. # Call callable object with N positional arguments: func(arg1, ..., argN)
  236. # Arguments are (func, arg1, ..., argN).
  237. py_call_op = custom_op(
  238. arg_types=[],
  239. return_type=object_rprimitive,
  240. c_function_name="PyObject_CallFunctionObjArgs",
  241. error_kind=ERR_MAGIC,
  242. var_arg_type=object_rprimitive,
  243. extra_int_constants=[(0, pointer_rprimitive)],
  244. )
  245. # Call callable object using positional and/or keyword arguments (Python 3.8+)
  246. py_vectorcall_op = custom_op(
  247. arg_types=[
  248. object_rprimitive, # Callable
  249. object_pointer_rprimitive, # Args (PyObject **)
  250. c_size_t_rprimitive, # Number of positional args
  251. object_rprimitive,
  252. ], # Keyword arg names tuple (or NULL)
  253. return_type=object_rprimitive,
  254. c_function_name="_PyObject_Vectorcall",
  255. error_kind=ERR_MAGIC,
  256. )
  257. # Call method using positional and/or keyword arguments (Python 3.9+)
  258. py_vectorcall_method_op = custom_op(
  259. arg_types=[
  260. object_rprimitive, # Method name
  261. object_pointer_rprimitive, # Args, including self (PyObject **)
  262. c_size_t_rprimitive, # Number of positional args, including self
  263. object_rprimitive,
  264. ], # Keyword arg names tuple (or NULL)
  265. return_type=object_rprimitive,
  266. c_function_name="PyObject_VectorcallMethod",
  267. error_kind=ERR_MAGIC,
  268. )
  269. # Call callable object with positional + keyword args: func(*args, **kwargs)
  270. # Arguments are (func, *args tuple, **kwargs dict).
  271. py_call_with_kwargs_op = custom_op(
  272. arg_types=[object_rprimitive, object_rprimitive, object_rprimitive],
  273. return_type=object_rprimitive,
  274. c_function_name="PyObject_Call",
  275. error_kind=ERR_MAGIC,
  276. )
  277. # Call method with positional arguments: obj.method(arg1, ...)
  278. # Arguments are (object, attribute name, arg1, ...).
  279. py_method_call_op = custom_op(
  280. arg_types=[],
  281. return_type=object_rprimitive,
  282. c_function_name="CPyObject_CallMethodObjArgs",
  283. error_kind=ERR_MAGIC,
  284. var_arg_type=object_rprimitive,
  285. extra_int_constants=[(0, pointer_rprimitive)],
  286. )
  287. # len(obj)
  288. generic_len_op = custom_op(
  289. arg_types=[object_rprimitive],
  290. return_type=int_rprimitive,
  291. c_function_name="CPyObject_Size",
  292. error_kind=ERR_MAGIC,
  293. )
  294. # len(obj)
  295. # same as generic_len_op, however return py_ssize_t
  296. generic_ssize_t_len_op = custom_op(
  297. arg_types=[object_rprimitive],
  298. return_type=c_pyssize_t_rprimitive,
  299. c_function_name="PyObject_Size",
  300. error_kind=ERR_NEG_INT,
  301. )
  302. # iter(obj)
  303. iter_op = function_op(
  304. name="builtins.iter",
  305. arg_types=[object_rprimitive],
  306. return_type=object_rprimitive,
  307. c_function_name="PyObject_GetIter",
  308. error_kind=ERR_MAGIC,
  309. )
  310. # next(iterator)
  311. #
  312. # Although the error_kind is set to be ERR_NEVER, this can actually
  313. # return NULL, and thus it must be checked using Branch.IS_ERROR.
  314. next_op = custom_op(
  315. arg_types=[object_rprimitive],
  316. return_type=object_rprimitive,
  317. c_function_name="PyIter_Next",
  318. error_kind=ERR_NEVER,
  319. )
  320. # next(iterator)
  321. #
  322. # Do a next, don't swallow StopIteration, but also don't propagate an
  323. # error. (N.B: This can still return NULL without an error to
  324. # represent an implicit StopIteration, but if StopIteration is
  325. # *explicitly* raised this will not swallow it.)
  326. # Can return NULL: see next_op.
  327. next_raw_op = custom_op(
  328. arg_types=[object_rprimitive],
  329. return_type=object_rprimitive,
  330. c_function_name="CPyIter_Next",
  331. error_kind=ERR_NEVER,
  332. )
  333. # this would be aiter(obj) if it existed
  334. aiter_op = custom_op(
  335. arg_types=[object_rprimitive],
  336. return_type=object_rprimitive,
  337. c_function_name="CPy_GetAIter",
  338. error_kind=ERR_MAGIC,
  339. )
  340. # this would be anext(obj) if it existed
  341. anext_op = custom_op(
  342. arg_types=[object_rprimitive],
  343. return_type=object_rprimitive,
  344. c_function_name="CPy_GetANext",
  345. error_kind=ERR_MAGIC,
  346. )