func_ir.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. """Intermediate representation of functions."""
  2. from __future__ import annotations
  3. from typing import Sequence
  4. from typing_extensions import Final
  5. from mypy.nodes import ARG_POS, ArgKind, Block, FuncDef
  6. from mypyc.common import BITMAP_BITS, JsonDict, bitmap_name, get_id_from_name, short_id_from_name
  7. from mypyc.ir.ops import (
  8. Assign,
  9. AssignMulti,
  10. BasicBlock,
  11. ControlOp,
  12. DeserMaps,
  13. LoadAddress,
  14. Register,
  15. Value,
  16. )
  17. from mypyc.ir.rtypes import RType, bitmap_rprimitive, deserialize_type
  18. from mypyc.namegen import NameGenerator
  19. class RuntimeArg:
  20. """Description of a function argument in IR.
  21. Argument kind is one of ARG_* constants defined in mypy.nodes.
  22. """
  23. def __init__(
  24. self, name: str, typ: RType, kind: ArgKind = ARG_POS, pos_only: bool = False
  25. ) -> None:
  26. self.name = name
  27. self.type = typ
  28. self.kind = kind
  29. self.pos_only = pos_only
  30. @property
  31. def optional(self) -> bool:
  32. return self.kind.is_optional()
  33. def __repr__(self) -> str:
  34. return "RuntimeArg(name={}, type={}, optional={!r}, pos_only={!r})".format(
  35. self.name, self.type, self.optional, self.pos_only
  36. )
  37. def serialize(self) -> JsonDict:
  38. return {
  39. "name": self.name,
  40. "type": self.type.serialize(),
  41. "kind": int(self.kind.value),
  42. "pos_only": self.pos_only,
  43. }
  44. @classmethod
  45. def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> RuntimeArg:
  46. return RuntimeArg(
  47. data["name"],
  48. deserialize_type(data["type"], ctx),
  49. ArgKind(data["kind"]),
  50. data["pos_only"],
  51. )
  52. class FuncSignature:
  53. """Signature of a function in IR."""
  54. # TODO: Track if method?
  55. def __init__(self, args: Sequence[RuntimeArg], ret_type: RType) -> None:
  56. self.args = tuple(args)
  57. self.ret_type = ret_type
  58. # Bitmap arguments are use to mark default values for arguments that
  59. # have types with overlapping error values.
  60. self.num_bitmap_args = num_bitmap_args(self.args)
  61. if self.num_bitmap_args:
  62. extra = [
  63. RuntimeArg(bitmap_name(i), bitmap_rprimitive, pos_only=True)
  64. for i in range(self.num_bitmap_args)
  65. ]
  66. self.args = self.args + tuple(reversed(extra))
  67. def real_args(self) -> tuple[RuntimeArg, ...]:
  68. """Return arguments without any synthetic bitmap arguments."""
  69. if self.num_bitmap_args:
  70. return self.args[: -self.num_bitmap_args]
  71. return self.args
  72. def bound_sig(self) -> "FuncSignature":
  73. if self.num_bitmap_args:
  74. return FuncSignature(self.args[1 : -self.num_bitmap_args], self.ret_type)
  75. else:
  76. return FuncSignature(self.args[1:], self.ret_type)
  77. def __repr__(self) -> str:
  78. return f"FuncSignature(args={self.args!r}, ret={self.ret_type!r})"
  79. def serialize(self) -> JsonDict:
  80. if self.num_bitmap_args:
  81. args = self.args[: -self.num_bitmap_args]
  82. else:
  83. args = self.args
  84. return {"args": [t.serialize() for t in args], "ret_type": self.ret_type.serialize()}
  85. @classmethod
  86. def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncSignature:
  87. return FuncSignature(
  88. [RuntimeArg.deserialize(arg, ctx) for arg in data["args"]],
  89. deserialize_type(data["ret_type"], ctx),
  90. )
  91. def num_bitmap_args(args: tuple[RuntimeArg, ...]) -> int:
  92. n = 0
  93. for arg in args:
  94. if arg.type.error_overlap and arg.kind.is_optional():
  95. n += 1
  96. return (n + (BITMAP_BITS - 1)) // BITMAP_BITS
  97. FUNC_NORMAL: Final = 0
  98. FUNC_STATICMETHOD: Final = 1
  99. FUNC_CLASSMETHOD: Final = 2
  100. class FuncDecl:
  101. """Declaration of a function in IR (without body or implementation).
  102. A function can be a regular module-level function, a method, a
  103. static method, a class method, or a property getter/setter.
  104. """
  105. def __init__(
  106. self,
  107. name: str,
  108. class_name: str | None,
  109. module_name: str,
  110. sig: FuncSignature,
  111. kind: int = FUNC_NORMAL,
  112. is_prop_setter: bool = False,
  113. is_prop_getter: bool = False,
  114. implicit: bool = False,
  115. ) -> None:
  116. self.name = name
  117. self.class_name = class_name
  118. self.module_name = module_name
  119. self.sig = sig
  120. self.kind = kind
  121. self.is_prop_setter = is_prop_setter
  122. self.is_prop_getter = is_prop_getter
  123. if class_name is None:
  124. self.bound_sig: FuncSignature | None = None
  125. else:
  126. if kind == FUNC_STATICMETHOD:
  127. self.bound_sig = sig
  128. else:
  129. self.bound_sig = sig.bound_sig()
  130. # If True, not present in the mypy AST and must be synthesized during irbuild
  131. # Currently only supported for property getters/setters
  132. self.implicit = implicit
  133. # This is optional because this will be set to the line number when the corresponding
  134. # FuncIR is created
  135. self._line: int | None = None
  136. @property
  137. def line(self) -> int:
  138. assert self._line is not None
  139. return self._line
  140. @line.setter
  141. def line(self, line: int) -> None:
  142. self._line = line
  143. @property
  144. def id(self) -> str:
  145. assert self.line is not None
  146. return get_id_from_name(self.name, self.fullname, self.line)
  147. @staticmethod
  148. def compute_shortname(class_name: str | None, name: str) -> str:
  149. return class_name + "." + name if class_name else name
  150. @property
  151. def shortname(self) -> str:
  152. return FuncDecl.compute_shortname(self.class_name, self.name)
  153. @property
  154. def fullname(self) -> str:
  155. return self.module_name + "." + self.shortname
  156. def cname(self, names: NameGenerator) -> str:
  157. partial_name = short_id_from_name(self.name, self.shortname, self._line)
  158. return names.private_name(self.module_name, partial_name)
  159. def serialize(self) -> JsonDict:
  160. return {
  161. "name": self.name,
  162. "class_name": self.class_name,
  163. "module_name": self.module_name,
  164. "sig": self.sig.serialize(),
  165. "kind": self.kind,
  166. "is_prop_setter": self.is_prop_setter,
  167. "is_prop_getter": self.is_prop_getter,
  168. "implicit": self.implicit,
  169. }
  170. # TODO: move this to FuncIR?
  171. @staticmethod
  172. def get_id_from_json(func_ir: JsonDict) -> str:
  173. """Get the id from the serialized FuncIR associated with this FuncDecl"""
  174. decl = func_ir["decl"]
  175. shortname = FuncDecl.compute_shortname(decl["class_name"], decl["name"])
  176. fullname = decl["module_name"] + "." + shortname
  177. return get_id_from_name(decl["name"], fullname, func_ir["line"])
  178. @classmethod
  179. def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncDecl:
  180. return FuncDecl(
  181. data["name"],
  182. data["class_name"],
  183. data["module_name"],
  184. FuncSignature.deserialize(data["sig"], ctx),
  185. data["kind"],
  186. data["is_prop_setter"],
  187. data["is_prop_getter"],
  188. data["implicit"],
  189. )
  190. class FuncIR:
  191. """Intermediate representation of a function with contextual information.
  192. Unlike FuncDecl, this includes the IR of the body (basic blocks).
  193. """
  194. def __init__(
  195. self,
  196. decl: FuncDecl,
  197. arg_regs: list[Register],
  198. blocks: list[BasicBlock],
  199. line: int = -1,
  200. traceback_name: str | None = None,
  201. ) -> None:
  202. # Declaration of the function, including the signature
  203. self.decl = decl
  204. # Registers for all the arguments to the function
  205. self.arg_regs = arg_regs
  206. # Body of the function
  207. self.blocks = blocks
  208. self.decl.line = line
  209. # The name that should be displayed for tracebacks that
  210. # include this function. Function will be omitted from
  211. # tracebacks if None.
  212. self.traceback_name = traceback_name
  213. @property
  214. def line(self) -> int:
  215. return self.decl.line
  216. @property
  217. def args(self) -> Sequence[RuntimeArg]:
  218. return self.decl.sig.args
  219. @property
  220. def ret_type(self) -> RType:
  221. return self.decl.sig.ret_type
  222. @property
  223. def class_name(self) -> str | None:
  224. return self.decl.class_name
  225. @property
  226. def sig(self) -> FuncSignature:
  227. return self.decl.sig
  228. @property
  229. def name(self) -> str:
  230. return self.decl.name
  231. @property
  232. def fullname(self) -> str:
  233. return self.decl.fullname
  234. @property
  235. def id(self) -> str:
  236. return self.decl.id
  237. def cname(self, names: NameGenerator) -> str:
  238. return self.decl.cname(names)
  239. def __repr__(self) -> str:
  240. if self.class_name:
  241. return f"<FuncIR {self.class_name}.{self.name}>"
  242. else:
  243. return f"<FuncIR {self.name}>"
  244. def serialize(self) -> JsonDict:
  245. # We don't include blocks in the serialized version
  246. return {
  247. "decl": self.decl.serialize(),
  248. "line": self.line,
  249. "traceback_name": self.traceback_name,
  250. }
  251. @classmethod
  252. def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncIR:
  253. return FuncIR(
  254. FuncDecl.deserialize(data["decl"], ctx), [], [], data["line"], data["traceback_name"]
  255. )
  256. INVALID_FUNC_DEF: Final = FuncDef("<INVALID_FUNC_DEF>", [], Block([]))
  257. def all_values(args: list[Register], blocks: list[BasicBlock]) -> list[Value]:
  258. """Return the set of all values that may be initialized in the blocks.
  259. This omits registers that are only read.
  260. """
  261. values: list[Value] = list(args)
  262. seen_registers = set(args)
  263. for block in blocks:
  264. for op in block.ops:
  265. if not isinstance(op, ControlOp):
  266. if isinstance(op, (Assign, AssignMulti)):
  267. if op.dest not in seen_registers:
  268. values.append(op.dest)
  269. seen_registers.add(op.dest)
  270. elif op.is_void:
  271. continue
  272. else:
  273. # If we take the address of a register, it might get initialized.
  274. if (
  275. isinstance(op, LoadAddress)
  276. and isinstance(op.src, Register)
  277. and op.src not in seen_registers
  278. ):
  279. values.append(op.src)
  280. seen_registers.add(op.src)
  281. values.append(op)
  282. return values
  283. def all_values_full(args: list[Register], blocks: list[BasicBlock]) -> list[Value]:
  284. """Return set of all values that are initialized or accessed."""
  285. values: list[Value] = list(args)
  286. seen_registers = set(args)
  287. for block in blocks:
  288. for op in block.ops:
  289. for source in op.sources():
  290. # Look for uninitialized registers that are accessed. Ignore
  291. # non-registers since we don't allow ops outside basic blocks.
  292. if isinstance(source, Register) and source not in seen_registers:
  293. values.append(source)
  294. seen_registers.add(source)
  295. if not isinstance(op, ControlOp):
  296. if isinstance(op, (Assign, AssignMulti)):
  297. if op.dest not in seen_registers:
  298. values.append(op.dest)
  299. seen_registers.add(op.dest)
  300. elif op.is_void:
  301. continue
  302. else:
  303. values.append(op)
  304. return values