Skip to content

Abstract

AbstractFrame dataclass

AbstractFrame(
    code: Statement,
    worklist: WorkList[Successor[ResultType]] = WorkList(),
    visited: dict[
        Block, set[Successor[ResultType]]
    ] = dict(),
    *,
    parent: Self | None = None,
    has_parent_access: bool = False,
    lineno_offset: int = 0,
    globals: dict[str, Any] = dict(),
    entries: dict[SSAValue, ValueType] = dict()
)

Bases: Frame[ResultType]

Interpreter frame for abstract interpreter.

This frame is used to store the state of the abstract interpreter. It contains the worklist of successors to be processed.

AbstractInterpreter dataclass

AbstractInterpreter(
    dialects: DialectGroup,
    *,
    fuel: int | None = None,
    debug: bool = False,
    max_depth: int = 128,
    max_python_recursion_depth: int = 8192
)

Bases: BaseInterpreter[AbstractFrameType, ResultType], ABC

Abstract interpreter for the IR.

This is a base class for implementing abstract interpreters for the IR. It provides a framework for implementing abstract interpreters given a bounded lattice type.

The abstract interpreter is a forward dataflow analysis that computes the abstract values for each SSA value in the IR. The abstract values are computed by evaluating the statements in the IR using the abstract lattice operations.

The abstract interpreter is implemented as a worklist algorithm. The worklist contains the successors of the current block to be processed. The abstract interpreter processes each successor by evaluating the statements in the block and updating the abstract values in the frame.

The abstract interpreter provides hooks for customizing the behavior of the interpreter. The prehook_succ and posthook_succ methods can be used to perform custom actions before and after processing a successor.

lattice class-attribute instance-attribute

lattice: type[BoundedLattice[ResultType]] = field(
    init=False
)

lattice type for the abstract interpreter.

eval_recursion_limit

eval_recursion_limit(
    frame: AbstractFrameType,
) -> tuple[AbstractFrameType, ResultType]

Return the value of recursion exception, e.g in concrete interpreter, it will raise an exception if the limit is reached; in type inference, it will return a special value.

Source code in src/kirin/interp/abstract.py
144
145
146
147
def eval_recursion_limit(
    self, frame: AbstractFrameType
) -> tuple[AbstractFrameType, ResultType]:
    return frame, self.lattice.bottom()

posthook_succ

posthook_succ(frame: AbstractFrameType, succ: Successor)

Hook called after processing a successor.

This method can be used to perform custom actions after processing a successor. It is called after evaluating the statements in the block.

Parameters:

Name Type Description Default
frame AbstractFrameType

The current frame of the interpreter.

required
succ Successor

The successor that was processed.

required
Source code in src/kirin/interp/abstract.py
 99
100
101
102
103
104
105
106
107
108
109
def posthook_succ(self, frame: AbstractFrameType, succ: Successor):
    """Hook called after processing a successor.

    This method can be used to perform custom actions after processing
    a successor. It is called after evaluating the statements in the block.

    Args:
        frame: The current frame of the interpreter.
        succ: The successor that was processed.
    """
    return

prehook_succ

prehook_succ(frame: AbstractFrameType, succ: Successor)

Hook called before processing a successor.

This method can be used to perform custom actions before processing a successor. It is called before evaluating the statements in the block.

Parameters:

Name Type Description Default
frame AbstractFrameType

The current frame of the interpreter.

required
succ Successor

The successor to be processed.

required
Source code in src/kirin/interp/abstract.py
87
88
89
90
91
92
93
94
95
96
97
def prehook_succ(self, frame: AbstractFrameType, succ: Successor):
    """Hook called before processing a successor.

    This method can be used to perform custom actions before processing
    a successor. It is called before evaluating the statements in the block.

    Args:
        frame: The current frame of the interpreter.
        succ: The successor to be processed.
    """
    return

run_ssacfg_region

run_ssacfg_region(
    frame: AbstractFrameType,
    region: Region,
    args: tuple[ResultType, ...],
) -> (
    tuple[ResultType, ...] | None | ReturnValue[ResultType]
)

This implements how to run a region with MLIR SSA CFG convention.

Parameters:

Name Type Description Default
frame FrameType

the current frame.

required
region Region

the region to run.

required
args tuple[ValueType, ...]

the arguments to the region.

required

Returns:

Type Description
tuple[ValueType, ...] | None | ReturnValue[ValueType]

tuple[ValueType, ...] | SpecialValue[ValueType]: the result of running the region.

when region returns tuple[ValueType, ...], it means the region terminates normally with YieldValue. When region returns ReturnValue, it means the region terminates and needs to pop the frame. Region cannot return Successor because reference to external region is not allowed.

Source code in src/kirin/interp/abstract.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
def run_ssacfg_region(
    self, frame: AbstractFrameType, region: Region, args: tuple[ResultType, ...]
) -> tuple[ResultType, ...] | None | ReturnValue[ResultType]:
    result = None
    frame.worklist.append(Successor(region.blocks[0], *args))
    while (succ := frame.worklist.pop()) is not None:
        if succ.block in frame.visited:
            if succ in frame.visited[succ.block]:
                continue
        else:
            frame.visited[succ.block] = set()
        self.prehook_succ(frame, succ)
        block_result = self.run_succ(frame, succ)
        if len(frame.visited[succ.block]) < 128:
            frame.visited[succ.block].add(succ)
        else:
            continue

        if isinstance(block_result, Successor):
            raise InterpreterError(
                "unexpected successor, successors should be in worklist"
            )

        result = self.join_results(result, block_result)
        self.posthook_succ(frame, succ)

    if isinstance(result, YieldValue):
        return result.values
    return result

run_succ

run_succ(
    frame: AbstractFrameType, succ: Successor
) -> SpecialValue[ResultType]

Run a successor within the current frame. Args: frame: the current frame. succ: the successor to run.

Returns:

Name Type Description
SpecialValue SpecialValue[ValueType]

the result of running the successor.

Source code in src/kirin/interp/abstract.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def run_succ(
    self, frame: AbstractFrameType, succ: Successor
) -> SpecialValue[ResultType]:
    frame.current_block = succ.block
    self.set_values(frame, succ.block.args, succ.block_args)
    for stmt in succ.block.stmts:
        if self.should_exec_stmt(stmt) is False:
            continue

        frame.current_stmt = stmt
        stmt_results = self.eval_stmt(frame, stmt)
        if isinstance(stmt_results, tuple):
            self.set_values(frame, stmt._results, stmt_results)
        elif stmt_results is None:
            continue  # empty result
        else:  # terminate
            return stmt_results
    return None

set_values

set_values(
    frame: AbstractFrameType,
    ssa: Iterable[SSAValue],
    results: Iterable[ResultType],
)

Set the abstract values for the given SSA values in the frame.

This method is used to customize how the abstract values are set in the frame. By default, the abstract values are set directly in the frame.

Source code in src/kirin/interp/abstract.py
130
131
132
133
134
135
136
137
138
139
140
141
142
def set_values(
    self,
    frame: AbstractFrameType,
    ssa: Iterable[SSAValue],
    results: Iterable[ResultType],
):
    """Set the abstract values for the given SSA values in the frame.

    This method is used to customize how the abstract values are set in
    the frame. By default, the abstract values are set directly in the
    frame.
    """
    frame.set_values(ssa, results)

should_exec_stmt

should_exec_stmt(stmt: Statement) -> bool

This method can be used to control which statements are executed during the abstract interpretation. By default, all statements are executed.

This method is useful when one wants to skip certain statements during the abstract interpretation and is certain that the skipped statements do not affect the final result. This would allow saving computation time and memory by not evaluating the skipped statements and their results.

Parameters:

Name Type Description Default
stmt Statement

The statement to be executed.

required

Returns:

Type Description
bool

True if the statement should be executed, False otherwise.

Source code in src/kirin/interp/abstract.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def should_exec_stmt(self, stmt: Statement) -> bool:
    """This method can be used to control which statements are executed
    during the abstract interpretation. By default, all statements are
    executed.

    This method is useful when one wants to skip certain statements
    during the abstract interpretation and is certain that the skipped
    statements do not affect the final result. This would allow saving
    computation time and memory by not evaluating the skipped statements
    and their results.

    Args:
        stmt: The statement to be executed.

    Returns:
        True if the statement should be executed, False otherwise.
    """
    return True