kirin-toolchain 0.13.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) hide show
  1. kirin/__init__.py +7 -0
  2. kirin/analysis/__init__.py +24 -0
  3. kirin/analysis/callgraph.py +61 -0
  4. kirin/analysis/cfg.py +112 -0
  5. kirin/analysis/const/__init__.py +20 -0
  6. kirin/analysis/const/_visitor.py +2 -0
  7. kirin/analysis/const/_visitor.pyi +8 -0
  8. kirin/analysis/const/lattice.py +219 -0
  9. kirin/analysis/const/prop.py +116 -0
  10. kirin/analysis/forward.py +100 -0
  11. kirin/analysis/typeinfer/__init__.py +5 -0
  12. kirin/analysis/typeinfer/analysis.py +90 -0
  13. kirin/analysis/typeinfer/solve.py +141 -0
  14. kirin/decl/__init__.py +108 -0
  15. kirin/decl/base.py +65 -0
  16. kirin/decl/camel2snake.py +2 -0
  17. kirin/decl/emit/__init__.py +0 -0
  18. kirin/decl/emit/_create_fn.py +29 -0
  19. kirin/decl/emit/_set_new_attribute.py +22 -0
  20. kirin/decl/emit/dialect.py +8 -0
  21. kirin/decl/emit/init.py +277 -0
  22. kirin/decl/emit/name.py +10 -0
  23. kirin/decl/emit/property.py +182 -0
  24. kirin/decl/emit/repr.py +31 -0
  25. kirin/decl/emit/traits.py +13 -0
  26. kirin/decl/emit/typecheck.py +77 -0
  27. kirin/decl/emit/verify.py +51 -0
  28. kirin/decl/info.py +346 -0
  29. kirin/decl/scan_fields.py +157 -0
  30. kirin/decl/verify.py +69 -0
  31. kirin/dialects/__init__.py +14 -0
  32. kirin/dialects/_pprint_helper.py +53 -0
  33. kirin/dialects/cf/__init__.py +20 -0
  34. kirin/dialects/cf/constprop.py +51 -0
  35. kirin/dialects/cf/dialect.py +3 -0
  36. kirin/dialects/cf/emit.py +58 -0
  37. kirin/dialects/cf/interp.py +24 -0
  38. kirin/dialects/cf/stmts.py +68 -0
  39. kirin/dialects/cf/typeinfer.py +27 -0
  40. kirin/dialects/eltype.py +23 -0
  41. kirin/dialects/func/__init__.py +20 -0
  42. kirin/dialects/func/attrs.py +39 -0
  43. kirin/dialects/func/constprop.py +138 -0
  44. kirin/dialects/func/dialect.py +3 -0
  45. kirin/dialects/func/emit.py +80 -0
  46. kirin/dialects/func/interp.py +68 -0
  47. kirin/dialects/func/stmts.py +233 -0
  48. kirin/dialects/func/typeinfer.py +124 -0
  49. kirin/dialects/ilist/__init__.py +33 -0
  50. kirin/dialects/ilist/_dialect.py +3 -0
  51. kirin/dialects/ilist/_wrapper.py +51 -0
  52. kirin/dialects/ilist/interp.py +85 -0
  53. kirin/dialects/ilist/lowering.py +25 -0
  54. kirin/dialects/ilist/passes.py +32 -0
  55. kirin/dialects/ilist/rewrite/__init__.py +3 -0
  56. kirin/dialects/ilist/rewrite/const.py +45 -0
  57. kirin/dialects/ilist/rewrite/list.py +38 -0
  58. kirin/dialects/ilist/rewrite/unroll.py +131 -0
  59. kirin/dialects/ilist/runtime.py +63 -0
  60. kirin/dialects/ilist/stmts.py +102 -0
  61. kirin/dialects/ilist/typeinfer.py +120 -0
  62. kirin/dialects/lowering/__init__.py +7 -0
  63. kirin/dialects/lowering/call.py +48 -0
  64. kirin/dialects/lowering/cf.py +206 -0
  65. kirin/dialects/lowering/func.py +134 -0
  66. kirin/dialects/math/__init__.py +41 -0
  67. kirin/dialects/math/_gen.py +176 -0
  68. kirin/dialects/math/dialect.py +3 -0
  69. kirin/dialects/math/interp.py +190 -0
  70. kirin/dialects/math/stmts.py +369 -0
  71. kirin/dialects/module.py +139 -0
  72. kirin/dialects/py/__init__.py +40 -0
  73. kirin/dialects/py/assertion.py +91 -0
  74. kirin/dialects/py/assign.py +103 -0
  75. kirin/dialects/py/attr.py +59 -0
  76. kirin/dialects/py/base.py +34 -0
  77. kirin/dialects/py/binop/__init__.py +23 -0
  78. kirin/dialects/py/binop/_dialect.py +3 -0
  79. kirin/dialects/py/binop/interp.py +60 -0
  80. kirin/dialects/py/binop/julia.py +33 -0
  81. kirin/dialects/py/binop/lowering.py +22 -0
  82. kirin/dialects/py/binop/stmts.py +79 -0
  83. kirin/dialects/py/binop/typeinfer.py +108 -0
  84. kirin/dialects/py/boolop.py +84 -0
  85. kirin/dialects/py/builtin.py +78 -0
  86. kirin/dialects/py/cmp/__init__.py +16 -0
  87. kirin/dialects/py/cmp/_dialect.py +3 -0
  88. kirin/dialects/py/cmp/interp.py +48 -0
  89. kirin/dialects/py/cmp/julia.py +33 -0
  90. kirin/dialects/py/cmp/lowering.py +45 -0
  91. kirin/dialects/py/cmp/stmts.py +62 -0
  92. kirin/dialects/py/constant.py +79 -0
  93. kirin/dialects/py/indexing.py +251 -0
  94. kirin/dialects/py/iterable.py +90 -0
  95. kirin/dialects/py/len.py +57 -0
  96. kirin/dialects/py/list/__init__.py +15 -0
  97. kirin/dialects/py/list/_dialect.py +3 -0
  98. kirin/dialects/py/list/interp.py +21 -0
  99. kirin/dialects/py/list/lowering.py +25 -0
  100. kirin/dialects/py/list/stmts.py +22 -0
  101. kirin/dialects/py/list/typeinfer.py +54 -0
  102. kirin/dialects/py/range.py +76 -0
  103. kirin/dialects/py/slice.py +120 -0
  104. kirin/dialects/py/tuple.py +109 -0
  105. kirin/dialects/py/unary/__init__.py +24 -0
  106. kirin/dialects/py/unary/_dialect.py +3 -0
  107. kirin/dialects/py/unary/constprop.py +20 -0
  108. kirin/dialects/py/unary/interp.py +24 -0
  109. kirin/dialects/py/unary/julia.py +21 -0
  110. kirin/dialects/py/unary/lowering.py +22 -0
  111. kirin/dialects/py/unary/stmts.py +33 -0
  112. kirin/dialects/py/unary/typeinfer.py +23 -0
  113. kirin/dialects/py/unpack.py +90 -0
  114. kirin/dialects/scf/__init__.py +23 -0
  115. kirin/dialects/scf/_dialect.py +3 -0
  116. kirin/dialects/scf/absint.py +64 -0
  117. kirin/dialects/scf/constprop.py +140 -0
  118. kirin/dialects/scf/interp.py +35 -0
  119. kirin/dialects/scf/lowering.py +123 -0
  120. kirin/dialects/scf/stmts.py +250 -0
  121. kirin/dialects/scf/trim.py +36 -0
  122. kirin/dialects/scf/typeinfer.py +58 -0
  123. kirin/dialects/scf/unroll.py +92 -0
  124. kirin/emit/__init__.py +3 -0
  125. kirin/emit/abc.py +89 -0
  126. kirin/emit/abc.pyi +38 -0
  127. kirin/emit/exceptions.py +5 -0
  128. kirin/emit/julia.py +63 -0
  129. kirin/emit/str.py +51 -0
  130. kirin/exceptions.py +59 -0
  131. kirin/graph.py +34 -0
  132. kirin/idtable.py +57 -0
  133. kirin/interp/__init__.py +39 -0
  134. kirin/interp/abstract.py +253 -0
  135. kirin/interp/base.py +438 -0
  136. kirin/interp/concrete.py +62 -0
  137. kirin/interp/exceptions.py +26 -0
  138. kirin/interp/frame.py +151 -0
  139. kirin/interp/impl.py +197 -0
  140. kirin/interp/result.py +93 -0
  141. kirin/interp/state.py +71 -0
  142. kirin/interp/table.py +40 -0
  143. kirin/interp/value.py +73 -0
  144. kirin/ir/__init__.py +46 -0
  145. kirin/ir/attrs/__init__.py +20 -0
  146. kirin/ir/attrs/_types.py +8 -0
  147. kirin/ir/attrs/_types.pyi +13 -0
  148. kirin/ir/attrs/abc.py +46 -0
  149. kirin/ir/attrs/py.py +45 -0
  150. kirin/ir/attrs/types.py +522 -0
  151. kirin/ir/dialect.py +125 -0
  152. kirin/ir/group.py +249 -0
  153. kirin/ir/method.py +118 -0
  154. kirin/ir/nodes/__init__.py +7 -0
  155. kirin/ir/nodes/base.py +149 -0
  156. kirin/ir/nodes/block.py +458 -0
  157. kirin/ir/nodes/region.py +337 -0
  158. kirin/ir/nodes/stmt.py +713 -0
  159. kirin/ir/nodes/view.py +142 -0
  160. kirin/ir/ssa.py +204 -0
  161. kirin/ir/traits/__init__.py +36 -0
  162. kirin/ir/traits/abc.py +42 -0
  163. kirin/ir/traits/basic.py +78 -0
  164. kirin/ir/traits/callable.py +51 -0
  165. kirin/ir/traits/lowering/__init__.py +2 -0
  166. kirin/ir/traits/lowering/call.py +37 -0
  167. kirin/ir/traits/lowering/context.py +120 -0
  168. kirin/ir/traits/region/__init__.py +2 -0
  169. kirin/ir/traits/region/ssacfg.py +22 -0
  170. kirin/ir/traits/symbol.py +57 -0
  171. kirin/ir/use.py +17 -0
  172. kirin/lattice/__init__.py +13 -0
  173. kirin/lattice/abc.py +128 -0
  174. kirin/lattice/empty.py +25 -0
  175. kirin/lattice/mixin.py +51 -0
  176. kirin/lowering/__init__.py +7 -0
  177. kirin/lowering/binding.py +65 -0
  178. kirin/lowering/core.py +72 -0
  179. kirin/lowering/dialect.py +35 -0
  180. kirin/lowering/dialect.pyi +183 -0
  181. kirin/lowering/frame.py +171 -0
  182. kirin/lowering/result.py +68 -0
  183. kirin/lowering/state.py +441 -0
  184. kirin/lowering/stream.py +53 -0
  185. kirin/passes/__init__.py +3 -0
  186. kirin/passes/abc.py +44 -0
  187. kirin/passes/aggressive/__init__.py +1 -0
  188. kirin/passes/aggressive/fold.py +43 -0
  189. kirin/passes/fold.py +45 -0
  190. kirin/passes/inline.py +25 -0
  191. kirin/passes/typeinfer.py +25 -0
  192. kirin/prelude.py +197 -0
  193. kirin/print/__init__.py +15 -0
  194. kirin/print/printable.py +141 -0
  195. kirin/print/printer.py +415 -0
  196. kirin/py.typed +0 -0
  197. kirin/registry.py +105 -0
  198. kirin/registry.pyi +52 -0
  199. kirin/rewrite/__init__.py +14 -0
  200. kirin/rewrite/abc.py +43 -0
  201. kirin/rewrite/aggressive/__init__.py +1 -0
  202. kirin/rewrite/aggressive/fold.py +43 -0
  203. kirin/rewrite/alias.py +16 -0
  204. kirin/rewrite/apply_type.py +47 -0
  205. kirin/rewrite/call2invoke.py +34 -0
  206. kirin/rewrite/chain.py +39 -0
  207. kirin/rewrite/compactify.py +288 -0
  208. kirin/rewrite/cse.py +48 -0
  209. kirin/rewrite/dce.py +19 -0
  210. kirin/rewrite/fixpoint.py +34 -0
  211. kirin/rewrite/fold.py +57 -0
  212. kirin/rewrite/getfield.py +21 -0
  213. kirin/rewrite/getitem.py +37 -0
  214. kirin/rewrite/inline.py +143 -0
  215. kirin/rewrite/result.py +15 -0
  216. kirin/rewrite/walk.py +83 -0
  217. kirin/rewrite/wrap_const.py +55 -0
  218. kirin/source.py +21 -0
  219. kirin/symbol_table.py +27 -0
  220. kirin/types.py +34 -0
  221. kirin/worklist.py +30 -0
  222. kirin_toolchain-0.13.0.dist-info/METADATA +42 -0
  223. kirin_toolchain-0.13.0.dist-info/RECORD +225 -0
  224. kirin_toolchain-0.13.0.dist-info/WHEEL +4 -0
  225. kirin_toolchain-0.13.0.dist-info/licenses/LICENSE +234 -0
kirin/exceptions.py ADDED
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from kirin.lowering import LoweringState
7
+ from kirin.ir.nodes.base import IRNode
8
+
9
+
10
+ class InterpreterExit(Exception):
11
+ pass
12
+
13
+
14
+ class DialectDefError(Exception):
15
+ pass
16
+
17
+
18
+ class DialectSyntaxError(Exception):
19
+ pass
20
+
21
+
22
+ class DialectLoweringError(Exception):
23
+
24
+ def append_hint(self, lowering: LoweringState):
25
+ msg = self.args[0]
26
+ source = lowering.source
27
+ if lowering.lines:
28
+ lines = lowering.lines
29
+ begin = max(0, source.lineno - lowering.max_lines)
30
+ end = min(
31
+ len(lines),
32
+ source.end_lineno or source.lineno + lowering.max_lines,
33
+ )
34
+ lines = (
35
+ lines[begin : source.lineno]
36
+ + [("-" * source.col_offset) + "^"]
37
+ + lines[lowering.lineno_offset : end]
38
+ )
39
+ trace = "\n".join(lines)
40
+ msg = f"{msg}: \n\n{trace}"
41
+ else:
42
+ msg = f"{msg}: {source.lineno}:{source.col_offset}"
43
+
44
+ self.args = (msg,)
45
+ return self
46
+
47
+
48
+ class CompilerError(Exception):
49
+ pass
50
+
51
+
52
+ class VerificationError(Exception):
53
+ def __init__(self, node: "IRNode", *messages: str) -> None:
54
+ super().__init__(*messages)
55
+ self.node = node
56
+
57
+
58
+ class DuplicatedDefinitionError(Exception):
59
+ pass
kirin/graph.py ADDED
@@ -0,0 +1,34 @@
1
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterable, Optional, Protocol
2
+
3
+ if TYPE_CHECKING:
4
+ from kirin import ir
5
+ from kirin.print import Printer
6
+
7
+ Node = TypeVar("Node")
8
+
9
+
10
+ class Graph(Protocol, Generic[Node]):
11
+ """The graph interface.
12
+
13
+ This interface defines the methods that a graph object must implement.
14
+ The graph interface is mainly for compatibility reasons so that one can
15
+ use multiple graph implementations interchangeably.
16
+ """
17
+
18
+ def get_neighbors(self, node: Node) -> Iterable[Node]:
19
+ """Get the neighbors of a node."""
20
+ ...
21
+
22
+ def get_nodes(self) -> Iterable[Node]:
23
+ """Get all the nodes in the graph."""
24
+ ...
25
+
26
+ def get_edges(self) -> Iterable[tuple[Node, Node]]:
27
+ """Get all the edges in the graph."""
28
+ ...
29
+
30
+ def print(
31
+ self,
32
+ printer: Optional["Printer"] = None,
33
+ analysis: dict["ir.SSAValue", Any] | None = None,
34
+ ) -> None: ...
kirin/idtable.py ADDED
@@ -0,0 +1,57 @@
1
+ from typing import Generic, TypeVar
2
+ from dataclasses import field, dataclass
3
+
4
+ T = TypeVar("T")
5
+
6
+
7
+ @dataclass
8
+ class IdTable(Generic[T]):
9
+ """A table that maps values to "human readable" unique names.
10
+ This is used for IR printing and code generation of SSA values
11
+ and basic blocks, or anything else required to have a unique name.
12
+
13
+ ## Example
14
+
15
+ ```python
16
+ from kirin import ir
17
+ from kirin.idtable import IdTable
18
+ table = IdTable()
19
+ x = ir.TestValue()
20
+ table[x] # "%0"
21
+ table[x] # "%0"
22
+ y = ir.TestValue()
23
+ table[y] # "%1"
24
+ ```
25
+ """
26
+
27
+ prefix: str = "%"
28
+ """The prefix to use for generated names."""
29
+ table: dict[T, str] = field(default_factory=dict)
30
+ """The table that maps values to names."""
31
+ name_count: dict[str, int] = field(default_factory=dict)
32
+ """The count of names that have been generated."""
33
+ next_id: int = 0
34
+ """The next ID to use for generating names."""
35
+ prefix_if_none: str = ""
36
+ """An alternate prefix to use when the name is None."""
37
+
38
+ def add(self, value: T) -> str:
39
+ """Add a value to the table and return the name."""
40
+ id = self.next_id
41
+ if (value_name := getattr(value, "name", None)) is not None:
42
+ curr_ind = self.name_count.get(value_name, 0)
43
+ suffix = f"_{curr_ind}" if curr_ind != 0 else ""
44
+ self.name_count[value_name] = curr_ind + 1
45
+ name = self.prefix + value_name + suffix
46
+ self.table[value] = name
47
+ else:
48
+ name = f"{self.prefix}{self.prefix_if_none}{id}"
49
+ self.next_id += 1
50
+ self.table[value] = name
51
+ return name
52
+
53
+ def __getitem__(self, value: T) -> str:
54
+ if value in self.table:
55
+ return self.table[value]
56
+ else:
57
+ return self.add(value)
@@ -0,0 +1,39 @@
1
+ """Interpreter module for Kirin.
2
+
3
+ This module contains the interpreter framework for Kirin. The interpreter
4
+ framework is used to implement concrete and abstract interpreters for the
5
+ IR. The interpreter framework provides a set of classes and interfaces to
6
+ implement interpreters for the IR.
7
+
8
+ The interpreter framework is designed to be extensible and customizable. It
9
+ provides a set of base classes and interfaces for implementing concrete and
10
+ abstract interpreters:
11
+
12
+ - [`BaseInterpreter`][kirin.interp.BaseInterpreter]: Base class for implementing concrete interpreters.
13
+ - [`AbstractInterpreter`][kirin.interp.AbstractInterpreter]: Base class for implementing abstract interpreters.
14
+ - [`Frame`][kirin.interp.Frame]: Base class for interpreter frame.
15
+ - [`MethodTable`][kirin.interp.MethodTable]: Method table for registering implementations of statements.
16
+ """
17
+
18
+ from . import result as result
19
+ from .base import BaseInterpreter as BaseInterpreter
20
+ from .impl import ImplDef as ImplDef, Signature as Signature, impl as impl
21
+ from .frame import Frame as Frame, FrameABC as FrameABC
22
+ from .table import MethodTable as MethodTable
23
+ from .value import (
24
+ Successor as Successor,
25
+ YieldValue as YieldValue,
26
+ ReturnValue as ReturnValue,
27
+ SpecialValue as SpecialValue,
28
+ StatementResult as StatementResult,
29
+ )
30
+ from .abstract import (
31
+ AbstractFrame as AbstractFrame,
32
+ AbstractInterpreter as AbstractInterpreter,
33
+ )
34
+ from .concrete import Interpreter as Interpreter
35
+ from .exceptions import (
36
+ WrapException as WrapException,
37
+ InterpreterError as InterpreterError,
38
+ FuelExhaustedError as FuelExhaustedError,
39
+ )
@@ -0,0 +1,253 @@
1
+ from abc import ABC
2
+ from typing import TypeVar, Iterable, TypeAlias, overload
3
+ from dataclasses import field, dataclass
4
+
5
+ from kirin.ir import Block, Region, SSAValue, Statement
6
+ from kirin.lattice import BoundedLattice
7
+ from kirin.worklist import WorkList
8
+ from kirin.interp.base import BaseInterpreter, InterpreterMeta
9
+ from kirin.interp.frame import Frame
10
+ from kirin.interp.value import Successor, YieldValue, ReturnValue, SpecialValue
11
+ from kirin.interp.exceptions import InterpreterError
12
+
13
+ ResultType = TypeVar("ResultType", bound=BoundedLattice)
14
+ WorkListType = TypeVar("WorkListType", bound=WorkList[Successor])
15
+
16
+ AbsIntResultType: TypeAlias = (
17
+ tuple[ResultType, ...] | None | ReturnValue[ResultType] | YieldValue[ResultType]
18
+ )
19
+
20
+
21
+ @dataclass
22
+ class AbstractFrame(Frame[ResultType]):
23
+ """Interpreter frame for abstract interpreter.
24
+
25
+ This frame is used to store the state of the abstract interpreter.
26
+ It contains the worklist of successors to be processed.
27
+ """
28
+
29
+ worklist: WorkList[Successor[ResultType]] = field(default_factory=WorkList)
30
+ visited: dict[Block, set[Successor[ResultType]]] = field(default_factory=dict)
31
+
32
+
33
+ AbstractFrameType = TypeVar("AbstractFrameType", bound=AbstractFrame)
34
+
35
+ # TODO: support custom loop termination heurestics, e.g. max iteration, etc.
36
+ # currently we may end up in infinite loop
37
+
38
+
39
+ class AbstractInterpreterMeta(InterpreterMeta):
40
+ pass
41
+
42
+
43
+ @dataclass
44
+ class AbstractInterpreter(
45
+ BaseInterpreter[AbstractFrameType, ResultType],
46
+ ABC,
47
+ metaclass=AbstractInterpreterMeta,
48
+ ):
49
+ """Abstract interpreter for the IR.
50
+
51
+ This is a base class for implementing abstract interpreters for the IR.
52
+ It provides a framework for implementing abstract interpreters given a
53
+ bounded lattice type.
54
+
55
+ The abstract interpreter is a forward dataflow analysis that computes
56
+ the abstract values for each SSA value in the IR. The abstract values
57
+ are computed by evaluating the statements in the IR using the abstract
58
+ lattice operations.
59
+
60
+ The abstract interpreter is implemented as a worklist algorithm. The
61
+ worklist contains the successors of the current block to be processed.
62
+ The abstract interpreter processes each successor by evaluating the
63
+ statements in the block and updating the abstract values in the frame.
64
+
65
+ The abstract interpreter provides hooks for customizing the behavior of
66
+ the interpreter.
67
+ The [`prehook_succ`][kirin.interp.abstract.AbstractInterpreter.prehook_succ] and
68
+ [`posthook_succ`][kirin.interp.abstract.AbstractInterpreter.posthook_succ] methods
69
+ can be used to perform custom actions before and after processing a successor.
70
+ """
71
+
72
+ lattice: type[BoundedLattice[ResultType]] = field(init=False)
73
+ """lattice type for the abstract interpreter.
74
+ """
75
+
76
+ def __init_subclass__(cls) -> None:
77
+ if ABC in cls.__bases__:
78
+ return super().__init_subclass__()
79
+
80
+ if not hasattr(cls, "lattice"):
81
+ raise TypeError(
82
+ f"missing lattice attribute in abstract interpreter class {cls}"
83
+ )
84
+ cls.void = cls.lattice.bottom()
85
+ super().__init_subclass__()
86
+
87
+ def prehook_succ(self, frame: AbstractFrameType, succ: Successor):
88
+ """Hook called before processing a successor.
89
+
90
+ This method can be used to perform custom actions before processing
91
+ a successor. It is called before evaluating the statements in the block.
92
+
93
+ Args:
94
+ frame: The current frame of the interpreter.
95
+ succ: The successor to be processed.
96
+ """
97
+ return
98
+
99
+ def posthook_succ(self, frame: AbstractFrameType, succ: Successor):
100
+ """Hook called after processing a successor.
101
+
102
+ This method can be used to perform custom actions after processing
103
+ a successor. It is called after evaluating the statements in the block.
104
+
105
+ Args:
106
+ frame: The current frame of the interpreter.
107
+ succ: The successor that was processed.
108
+ """
109
+ return
110
+
111
+ def should_exec_stmt(self, stmt: Statement) -> bool:
112
+ """This method can be used to control which statements are executed
113
+ during the abstract interpretation. By default, all statements are
114
+ executed.
115
+
116
+ This method is useful when one wants to skip certain statements
117
+ during the abstract interpretation and is certain that the skipped
118
+ statements do not affect the final result. This would allow saving
119
+ computation time and memory by not evaluating the skipped statements
120
+ and their results.
121
+
122
+ Args:
123
+ stmt: The statement to be executed.
124
+
125
+ Returns:
126
+ True if the statement should be executed, False otherwise.
127
+ """
128
+ return True
129
+
130
+ def set_values(
131
+ self,
132
+ frame: AbstractFrameType,
133
+ ssa: Iterable[SSAValue],
134
+ results: Iterable[ResultType],
135
+ ):
136
+ """Set the abstract values for the given SSA values in the frame.
137
+
138
+ This method is used to customize how the abstract values are set in
139
+ the frame. By default, the abstract values are set directly in the
140
+ frame.
141
+ """
142
+ frame.set_values(ssa, results)
143
+
144
+ def eval_recursion_limit(
145
+ self, frame: AbstractFrameType
146
+ ) -> tuple[AbstractFrameType, ResultType]:
147
+ return frame, self.lattice.bottom()
148
+
149
+ def run_ssacfg_region(
150
+ self, frame: AbstractFrameType, region: Region
151
+ ) -> tuple[ResultType, ...] | None | ReturnValue[ResultType]:
152
+ result = None
153
+ frame.worklist.append(
154
+ Successor(region.blocks[0], *frame.get_values(region.blocks[0].args))
155
+ )
156
+ while (succ := frame.worklist.pop()) is not None:
157
+ if succ.block in frame.visited:
158
+ if succ in frame.visited[succ.block]:
159
+ continue
160
+ else:
161
+ frame.visited[succ.block] = set()
162
+ self.prehook_succ(frame, succ)
163
+ block_result = self.run_block(frame, succ)
164
+ if len(frame.visited[succ.block]) < 128:
165
+ frame.visited[succ.block].add(succ)
166
+ else:
167
+ continue
168
+
169
+ if isinstance(block_result, Successor):
170
+ raise InterpreterError(
171
+ "unexpected successor, successors should be in worklist"
172
+ )
173
+
174
+ result = self.join_results(result, block_result)
175
+ self.posthook_succ(frame, succ)
176
+
177
+ if isinstance(result, YieldValue):
178
+ return result.values
179
+ return result
180
+
181
+ def run_block(
182
+ self, frame: AbstractFrameType, succ: Successor
183
+ ) -> SpecialValue[ResultType]:
184
+ self.set_values(frame, succ.block.args, succ.block_args)
185
+ for stmt in succ.block.stmts:
186
+ if self.should_exec_stmt(stmt) is False:
187
+ continue
188
+
189
+ frame.stmt = stmt
190
+ frame.lino = stmt.source.lineno if stmt.source else 0
191
+ stmt_results = self.eval_stmt(frame, stmt)
192
+ if isinstance(stmt_results, tuple):
193
+ self.set_values(frame, stmt._results, stmt_results)
194
+ elif stmt_results is None:
195
+ continue # empty result
196
+ else: # terminate
197
+ return stmt_results
198
+ return None
199
+
200
+ @overload
201
+ def join_results(self, old: None, new: None) -> None: ...
202
+ @overload
203
+ def join_results(
204
+ self, old: ReturnValue[ResultType], new: ReturnValue[ResultType]
205
+ ) -> ReturnValue[ResultType]: ...
206
+ @overload
207
+ def join_results(
208
+ self, old: YieldValue[ResultType], new: YieldValue[ResultType]
209
+ ) -> YieldValue[ResultType]: ...
210
+ @overload
211
+ def join_results(
212
+ self, old: tuple[ResultType], new: tuple[ResultType]
213
+ ) -> tuple[ResultType]: ...
214
+ @overload
215
+ def join_results(
216
+ self,
217
+ old: tuple[ResultType, ...] | ReturnValue[ResultType] | None,
218
+ new: tuple[ResultType, ...] | ReturnValue[ResultType] | None,
219
+ ) -> tuple[ResultType, ...] | ReturnValue[ResultType] | None: ...
220
+ @overload
221
+ def join_results(
222
+ self,
223
+ old: ReturnValue[ResultType] | YieldValue[ResultType] | None,
224
+ new: ReturnValue[ResultType] | YieldValue[ResultType] | None,
225
+ ) -> ReturnValue[ResultType] | YieldValue[ResultType] | None: ...
226
+ @overload
227
+ def join_results(
228
+ self, old: AbsIntResultType[ResultType], new: AbsIntResultType[ResultType]
229
+ ) -> AbsIntResultType[ResultType]: ...
230
+
231
+ def join_results(
232
+ self,
233
+ old: AbsIntResultType[ResultType],
234
+ new: AbsIntResultType[ResultType],
235
+ ) -> AbsIntResultType[ResultType]:
236
+ if old is None:
237
+ return new
238
+ elif new is None:
239
+ return old
240
+
241
+ if isinstance(old, ReturnValue) and isinstance(new, ReturnValue):
242
+ return ReturnValue(old.value.join(new.value))
243
+ elif isinstance(old, YieldValue) and isinstance(new, YieldValue):
244
+ return YieldValue(
245
+ tuple(
246
+ old_val.join(new_val)
247
+ for old_val, new_val in zip(old.values, new.values)
248
+ )
249
+ )
250
+ elif isinstance(old, tuple) and isinstance(new, tuple):
251
+ return tuple(old_val.join(new_val) for old_val, new_val in zip(old, new))
252
+ else:
253
+ return None