func_ir.py 11 KB

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