registry.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. """Utilities for defining primitive ops.
  2. Most of the ops can be automatically generated by matching against AST
  3. nodes and types. For example, a func_op is automatically generated when
  4. a specific function is called with the specific positional argument
  5. count and argument types.
  6. Example op definition:
  7. list_len_op = func_op(name='builtins.len',
  8. arg_types=[list_rprimitive],
  9. result_type=short_int_rprimitive,
  10. error_kind=ERR_NEVER,
  11. emit=emit_len)
  12. This op is automatically generated for calls to len() with a single
  13. list argument. The result type is short_int_rprimitive, and this
  14. never raises an exception (ERR_NEVER). The function emit_len is used
  15. to generate C for this op. The op can also be manually generated using
  16. "list_len_op". Ops that are only generated automatically don't need to
  17. be assigned to a module attribute.
  18. Ops defined with custom_op are only explicitly generated in
  19. mypyc.irbuild and won't be generated automatically. They are always
  20. assigned to a module attribute, as otherwise they won't be accessible.
  21. The actual ops are defined in other submodules of this package, grouped
  22. by category.
  23. Most operations have fallback implementations that apply to all possible
  24. arguments and types. For example, there are generic implementations of
  25. arbitrary function and method calls, and binary operators. These generic
  26. implementations are typically slower than specialized ones, but we tend
  27. to rely on them for infrequently used ops. It's impractical to have
  28. optimized implementations of all ops.
  29. """
  30. from __future__ import annotations
  31. from typing import List, NamedTuple, Optional, Tuple
  32. from typing_extensions import Final
  33. from mypyc.ir.ops import StealsDescription
  34. from mypyc.ir.rtypes import RType
  35. # Error kind for functions that return negative integer on exception. This
  36. # is only used for primitives. We translate it away during IR building.
  37. ERR_NEG_INT: Final = 10
  38. class CFunctionDescription(NamedTuple):
  39. name: str
  40. arg_types: List[RType]
  41. return_type: RType
  42. var_arg_type: Optional[RType]
  43. truncated_type: Optional[RType]
  44. c_function_name: str
  45. error_kind: int
  46. steals: StealsDescription
  47. is_borrowed: bool
  48. ordering: Optional[List[int]]
  49. extra_int_constants: List[Tuple[int, RType]]
  50. priority: int
  51. # A description for C load operations including LoadGlobal and LoadAddress
  52. class LoadAddressDescription(NamedTuple):
  53. name: str
  54. type: RType
  55. src: str # name of the target to load
  56. # CallC op for method call(such as 'str.join')
  57. method_call_ops: dict[str, list[CFunctionDescription]] = {}
  58. # CallC op for top level function call(such as 'builtins.list')
  59. function_ops: dict[str, list[CFunctionDescription]] = {}
  60. # CallC op for binary ops
  61. binary_ops: dict[str, list[CFunctionDescription]] = {}
  62. # CallC op for unary ops
  63. unary_ops: dict[str, list[CFunctionDescription]] = {}
  64. builtin_names: dict[str, tuple[RType, str]] = {}
  65. def method_op(
  66. name: str,
  67. arg_types: list[RType],
  68. return_type: RType,
  69. c_function_name: str,
  70. error_kind: int,
  71. var_arg_type: RType | None = None,
  72. truncated_type: RType | None = None,
  73. ordering: list[int] | None = None,
  74. extra_int_constants: list[tuple[int, RType]] = [],
  75. steals: StealsDescription = False,
  76. is_borrowed: bool = False,
  77. priority: int = 1,
  78. ) -> CFunctionDescription:
  79. """Define a c function call op that replaces a method call.
  80. This will be automatically generated by matching against the AST.
  81. Args:
  82. name: short name of the method (for example, 'append')
  83. arg_types: argument types; the receiver is always the first argument
  84. return_type: type of the return value. Use void_rtype to represent void.
  85. c_function_name: name of the C function to call
  86. error_kind: how errors are represented in the result (one of ERR_*)
  87. var_arg_type: type of all variable arguments
  88. truncated_type: type to truncated to(See Truncate for info)
  89. if it's defined both return_type and it should be non-referenced
  90. integer types or bool type
  91. ordering: optional ordering of the arguments, if defined,
  92. reorders the arguments accordingly.
  93. should never be used together with var_arg_type.
  94. all the other arguments(such as arg_types) are in the order
  95. accepted by the python syntax(before reordering)
  96. extra_int_constants: optional extra integer constants as the last arguments to a C call
  97. steals: description of arguments that this steals (ref count wise)
  98. is_borrowed: if True, returned value is borrowed (no need to decrease refcount)
  99. priority: if multiple ops match, the one with the highest priority is picked
  100. """
  101. ops = method_call_ops.setdefault(name, [])
  102. desc = CFunctionDescription(
  103. name,
  104. arg_types,
  105. return_type,
  106. var_arg_type,
  107. truncated_type,
  108. c_function_name,
  109. error_kind,
  110. steals,
  111. is_borrowed,
  112. ordering,
  113. extra_int_constants,
  114. priority,
  115. )
  116. ops.append(desc)
  117. return desc
  118. def function_op(
  119. name: str,
  120. arg_types: list[RType],
  121. return_type: RType,
  122. c_function_name: str,
  123. error_kind: int,
  124. var_arg_type: RType | None = None,
  125. truncated_type: RType | None = None,
  126. ordering: list[int] | None = None,
  127. extra_int_constants: list[tuple[int, RType]] = [],
  128. steals: StealsDescription = False,
  129. is_borrowed: bool = False,
  130. priority: int = 1,
  131. ) -> CFunctionDescription:
  132. """Define a c function call op that replaces a function call.
  133. This will be automatically generated by matching against the AST.
  134. Most arguments are similar to method_op().
  135. Args:
  136. name: full name of the function
  137. arg_types: positional argument types for which this applies
  138. """
  139. ops = function_ops.setdefault(name, [])
  140. desc = CFunctionDescription(
  141. name,
  142. arg_types,
  143. return_type,
  144. var_arg_type,
  145. truncated_type,
  146. c_function_name,
  147. error_kind,
  148. steals,
  149. is_borrowed,
  150. ordering,
  151. extra_int_constants,
  152. priority,
  153. )
  154. ops.append(desc)
  155. return desc
  156. def binary_op(
  157. name: str,
  158. arg_types: list[RType],
  159. return_type: RType,
  160. c_function_name: str,
  161. error_kind: int,
  162. var_arg_type: RType | None = None,
  163. truncated_type: RType | None = None,
  164. ordering: list[int] | None = None,
  165. extra_int_constants: list[tuple[int, RType]] = [],
  166. steals: StealsDescription = False,
  167. is_borrowed: bool = False,
  168. priority: int = 1,
  169. ) -> CFunctionDescription:
  170. """Define a c function call op for a binary operation.
  171. This will be automatically generated by matching against the AST.
  172. Most arguments are similar to method_op(), but exactly two argument types
  173. are expected.
  174. """
  175. ops = binary_ops.setdefault(name, [])
  176. desc = CFunctionDescription(
  177. name,
  178. arg_types,
  179. return_type,
  180. var_arg_type,
  181. truncated_type,
  182. c_function_name,
  183. error_kind,
  184. steals,
  185. is_borrowed,
  186. ordering,
  187. extra_int_constants,
  188. priority,
  189. )
  190. ops.append(desc)
  191. return desc
  192. def custom_op(
  193. arg_types: list[RType],
  194. return_type: RType,
  195. c_function_name: str,
  196. error_kind: int,
  197. var_arg_type: RType | None = None,
  198. truncated_type: RType | None = None,
  199. ordering: list[int] | None = None,
  200. extra_int_constants: list[tuple[int, RType]] = [],
  201. steals: StealsDescription = False,
  202. is_borrowed: bool = False,
  203. ) -> CFunctionDescription:
  204. """Create a one-off CallC op that can't be automatically generated from the AST.
  205. Most arguments are similar to method_op().
  206. """
  207. return CFunctionDescription(
  208. "<custom>",
  209. arg_types,
  210. return_type,
  211. var_arg_type,
  212. truncated_type,
  213. c_function_name,
  214. error_kind,
  215. steals,
  216. is_borrowed,
  217. ordering,
  218. extra_int_constants,
  219. 0,
  220. )
  221. def unary_op(
  222. name: str,
  223. arg_type: RType,
  224. return_type: RType,
  225. c_function_name: str,
  226. error_kind: int,
  227. truncated_type: RType | None = None,
  228. ordering: list[int] | None = None,
  229. extra_int_constants: list[tuple[int, RType]] = [],
  230. steals: StealsDescription = False,
  231. is_borrowed: bool = False,
  232. priority: int = 1,
  233. ) -> CFunctionDescription:
  234. """Define a c function call op for an unary operation.
  235. This will be automatically generated by matching against the AST.
  236. Most arguments are similar to method_op(), but exactly one argument type
  237. is expected.
  238. """
  239. ops = unary_ops.setdefault(name, [])
  240. desc = CFunctionDescription(
  241. name,
  242. [arg_type],
  243. return_type,
  244. None,
  245. truncated_type,
  246. c_function_name,
  247. error_kind,
  248. steals,
  249. is_borrowed,
  250. ordering,
  251. extra_int_constants,
  252. priority,
  253. )
  254. ops.append(desc)
  255. return desc
  256. def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription:
  257. assert name not in builtin_names, "already defined: %s" % name
  258. builtin_names[name] = (type, src)
  259. return LoadAddressDescription(name, type, src)
  260. import mypyc.primitives.bytes_ops
  261. import mypyc.primitives.dict_ops
  262. import mypyc.primitives.float_ops
  263. # Import various modules that set up global state.
  264. import mypyc.primitives.int_ops
  265. import mypyc.primitives.list_ops
  266. import mypyc.primitives.misc_ops
  267. import mypyc.primitives.str_ops
  268. import mypyc.primitives.tuple_ops # noqa: F401