angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.141__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +136 -53
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +11 -0
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
- angr/analyses/decompiler/ail_simplifier.py +115 -72
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +78 -43
- angr/analyses/decompiler/decompiler.py +18 -7
- angr/analyses/decompiler/expression_narrower.py +1 -1
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
- angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +6 -4
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
- angr/analyses/decompiler/ssailification/rewriting.py +23 -15
- angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/structured_codegen/c.py +73 -137
- angr/analyses/decompiler/structuring/dream.py +22 -18
- angr/analyses/decompiler/structuring/phoenix.py +158 -41
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +37 -10
- angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
- angr/analyses/decompiler/utils.py +106 -21
- angr/analyses/deobfuscator/api_obf_finder.py +8 -5
- angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
- angr/analyses/deobfuscator/string_obf_finder.py +105 -18
- angr/analyses/forward_analysis/forward_analysis.py +1 -1
- angr/analyses/propagator/top_checker_mixin.py +6 -6
- angr/analyses/reaching_definitions/__init__.py +2 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -12
- angr/analyses/reaching_definitions/engine_vex.py +36 -31
- angr/analyses/reaching_definitions/function_handler.py +15 -2
- angr/analyses/reaching_definitions/rd_state.py +1 -37
- angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
- angr/analyses/s_propagator.py +6 -41
- angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/stack_pointer_tracker.py +36 -22
- angr/analyses/typehoon/simple_solver.py +45 -7
- angr/analyses/typehoon/typeconsts.py +18 -5
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_base.py +7 -5
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +30 -11
- angr/distributed/__init__.py +1 -1
- angr/engines/__init__.py +7 -8
- angr/engines/engine.py +1 -120
- angr/engines/failure.py +2 -2
- angr/engines/hook.py +2 -2
- angr/engines/light/engine.py +2 -2
- angr/engines/pcode/engine.py +2 -14
- angr/engines/procedure.py +2 -2
- angr/engines/soot/engine.py +2 -2
- angr/engines/soot/statements/switch.py +1 -1
- angr/engines/successors.py +124 -11
- angr/engines/syscall.py +2 -2
- angr/engines/unicorn.py +3 -3
- angr/engines/vex/heavy/heavy.py +3 -15
- angr/factory.py +12 -22
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/knowledge_plugins/variables/variable_manager.py +7 -5
- angr/sim_type.py +19 -17
- angr/simos/simos.py +3 -1
- angr/state_plugins/plugin.py +19 -4
- angr/storage/memory_mixins/memory_mixin.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
- angr/utils/ssa/__init__.py +119 -4
- angr/utils/types.py +48 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
# pylint:disable=missing-class-docstring,unused-argument
|
|
1
|
+
# pylint:disable=missing-class-docstring,unused-argument,consider-using-dict-items
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from collections import defaultdict
|
|
4
|
-
from typing import Any, TYPE_CHECKING
|
|
5
4
|
from collections.abc import Iterable
|
|
5
|
+
from typing import Any, TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
import ailment
|
|
8
8
|
from ailment import Expression, Block, AILBlockWalker
|
|
9
|
-
from ailment.expression import ITE
|
|
10
|
-
from ailment.statement import Statement, Assignment, Call
|
|
9
|
+
from ailment.expression import ITE, Load
|
|
10
|
+
from ailment.statement import Statement, Assignment, Call, Return
|
|
11
11
|
|
|
12
12
|
from angr.utils.ail import is_phi_assignment
|
|
13
|
+
from angr.utils.ssa import VVarUsesCollector
|
|
13
14
|
from angr.analyses.decompiler.sequence_walker import SequenceWalker
|
|
14
15
|
from angr.analyses.decompiler.structuring.structurer_nodes import (
|
|
15
16
|
ConditionNode,
|
|
@@ -20,8 +21,6 @@ from angr.analyses.decompiler.structuring.structurer_nodes import (
|
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
23
|
-
from angr.sim_variable import SimVariable
|
|
24
|
-
from angr.knowledge_plugins.variables.variable_manager import VariableManagerInternal
|
|
25
24
|
from ailment.expression import MultiStatementExpression
|
|
26
25
|
|
|
27
26
|
|
|
@@ -30,22 +29,28 @@ class LocationBase:
|
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class StatementLocation(LocationBase):
|
|
32
|
+
"""
|
|
33
|
+
Describes the location of a statement.
|
|
34
|
+
"""
|
|
35
|
+
|
|
33
36
|
__slots__ = (
|
|
34
37
|
"block_addr",
|
|
35
38
|
"block_idx",
|
|
39
|
+
"phi_stmt",
|
|
36
40
|
"stmt_idx",
|
|
37
41
|
)
|
|
38
42
|
|
|
39
|
-
def __init__(self, block_addr, block_idx, stmt_idx):
|
|
43
|
+
def __init__(self, block_addr, block_idx, stmt_idx, phi_stmt: bool = False):
|
|
40
44
|
self.block_addr = block_addr
|
|
41
45
|
self.block_idx = block_idx
|
|
42
46
|
self.stmt_idx = stmt_idx
|
|
47
|
+
self.phi_stmt = phi_stmt
|
|
43
48
|
|
|
44
49
|
def __repr__(self):
|
|
45
|
-
return f"Loc: Statement@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}"
|
|
50
|
+
return f"Loc: Statement@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}{' phi' if self.phi_stmt else ''}"
|
|
46
51
|
|
|
47
52
|
def __hash__(self):
|
|
48
|
-
return hash((StatementLocation, self.block_addr, self.block_idx, self.stmt_idx))
|
|
53
|
+
return hash((StatementLocation, self.block_addr, self.block_idx, self.stmt_idx, self.phi_stmt))
|
|
49
54
|
|
|
50
55
|
def __eq__(self, other):
|
|
51
56
|
return (
|
|
@@ -53,34 +58,44 @@ class StatementLocation(LocationBase):
|
|
|
53
58
|
and self.block_addr == other.block_addr
|
|
54
59
|
and self.block_idx == other.block_idx
|
|
55
60
|
and self.stmt_idx == other.stmt_idx
|
|
61
|
+
and self.phi_stmt == other.phi_stmt
|
|
56
62
|
)
|
|
57
63
|
|
|
58
64
|
def copy(self):
|
|
59
|
-
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx)
|
|
65
|
+
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx, phi_stmt=self.phi_stmt)
|
|
60
66
|
|
|
61
67
|
|
|
62
68
|
class ExpressionLocation(LocationBase):
|
|
69
|
+
"""
|
|
70
|
+
Describes the location of an expression.
|
|
71
|
+
"""
|
|
72
|
+
|
|
63
73
|
__slots__ = (
|
|
64
74
|
"block_addr",
|
|
65
75
|
"block_idx",
|
|
66
76
|
"expr_idx",
|
|
77
|
+
"phi_stmt",
|
|
67
78
|
"stmt_idx",
|
|
68
79
|
)
|
|
69
80
|
|
|
70
|
-
def __init__(self, block_addr, block_idx, stmt_idx, expr_idx):
|
|
81
|
+
def __init__(self, block_addr, block_idx, stmt_idx, expr_idx, phi_stmt: bool = False):
|
|
71
82
|
self.block_addr = block_addr
|
|
72
83
|
self.block_idx = block_idx
|
|
73
84
|
self.stmt_idx = stmt_idx
|
|
74
85
|
self.expr_idx = expr_idx
|
|
86
|
+
self.phi_stmt = phi_stmt
|
|
75
87
|
|
|
76
88
|
def __repr__(self):
|
|
77
|
-
return
|
|
89
|
+
return (
|
|
90
|
+
f"Loc: Expression@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}[{self.expr_idx}]"
|
|
91
|
+
f"{'phi' if self.phi_stmt else ''}"
|
|
92
|
+
)
|
|
78
93
|
|
|
79
94
|
def statement_location(self) -> StatementLocation:
|
|
80
|
-
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx)
|
|
95
|
+
return StatementLocation(self.block_addr, self.block_idx, self.stmt_idx, phi_stmt=self.phi_stmt)
|
|
81
96
|
|
|
82
97
|
def __hash__(self):
|
|
83
|
-
return hash((ExpressionLocation, self.block_addr, self.block_idx, self.stmt_idx, self.expr_idx))
|
|
98
|
+
return hash((ExpressionLocation, self.block_addr, self.block_idx, self.stmt_idx, self.expr_idx, self.phi_stmt))
|
|
84
99
|
|
|
85
100
|
def __eq__(self, other):
|
|
86
101
|
return (
|
|
@@ -89,10 +104,15 @@ class ExpressionLocation(LocationBase):
|
|
|
89
104
|
and self.block_idx == other.block_idx
|
|
90
105
|
and self.stmt_idx == other.stmt_idx
|
|
91
106
|
and self.expr_idx == other.expr_idx
|
|
107
|
+
and self.phi_stmt == other.phi_stmt
|
|
92
108
|
)
|
|
93
109
|
|
|
94
110
|
|
|
95
111
|
class ConditionLocation(LocationBase):
|
|
112
|
+
"""
|
|
113
|
+
Describes the location of a condition.
|
|
114
|
+
"""
|
|
115
|
+
|
|
96
116
|
__slots__ = (
|
|
97
117
|
"case_idx",
|
|
98
118
|
"node_addr",
|
|
@@ -117,6 +137,10 @@ class ConditionLocation(LocationBase):
|
|
|
117
137
|
|
|
118
138
|
|
|
119
139
|
class ConditionalBreakLocation(LocationBase):
|
|
140
|
+
"""
|
|
141
|
+
Describes the location of a conditional break.
|
|
142
|
+
"""
|
|
143
|
+
|
|
120
144
|
__slots__ = ("node_addr",)
|
|
121
145
|
|
|
122
146
|
def __init__(self, node_addr):
|
|
@@ -177,18 +201,29 @@ class ExpressionUseFinder(AILBlockWalker):
|
|
|
177
201
|
|
|
178
202
|
def __init__(self):
|
|
179
203
|
super().__init__()
|
|
180
|
-
self.uses: defaultdict[
|
|
204
|
+
self.uses: defaultdict[int, set[tuple[Expression, ExpressionLocation | None]]] = defaultdict(set)
|
|
181
205
|
self.has_load = False
|
|
182
206
|
|
|
183
207
|
def _handle_expr(
|
|
184
208
|
self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
|
|
185
209
|
) -> Any:
|
|
186
|
-
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg
|
|
210
|
+
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg:
|
|
187
211
|
if not (isinstance(stmt, ailment.Stmt.Assignment) and stmt.dst is expr):
|
|
188
212
|
if block is not None:
|
|
189
|
-
self.uses[expr.
|
|
213
|
+
self.uses[expr.varid].add(
|
|
214
|
+
(
|
|
215
|
+
expr,
|
|
216
|
+
ExpressionLocation(
|
|
217
|
+
block.addr,
|
|
218
|
+
block.idx,
|
|
219
|
+
stmt_idx,
|
|
220
|
+
expr_idx,
|
|
221
|
+
phi_stmt=stmt is not None and is_phi_assignment(stmt),
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
)
|
|
190
225
|
else:
|
|
191
|
-
self.uses[expr.
|
|
226
|
+
self.uses[expr.varid].add((expr, None))
|
|
192
227
|
return None
|
|
193
228
|
return super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
|
|
194
229
|
|
|
@@ -202,7 +237,7 @@ class ExpressionCounter(SequenceWalker):
|
|
|
202
237
|
Find all expressions that are assigned once and only used once.
|
|
203
238
|
"""
|
|
204
239
|
|
|
205
|
-
def __init__(self, node
|
|
240
|
+
def __init__(self, node):
|
|
206
241
|
handlers = {
|
|
207
242
|
ConditionalBreakNode: self._handle_ConditionalBreak,
|
|
208
243
|
ConditionNode: self._handle_Condition,
|
|
@@ -215,94 +250,73 @@ class ExpressionCounter(SequenceWalker):
|
|
|
215
250
|
# the current assignment depends on, StatementLocation of the assignment statement, a Boolean variable that
|
|
216
251
|
# indicates if ExpressionUseFinder has succeeded or not)
|
|
217
252
|
self.assignments: defaultdict[Any, set[tuple]] = defaultdict(set)
|
|
218
|
-
self.uses: dict[
|
|
219
|
-
self._variable_manager: VariableManagerInternal = variable_manager
|
|
253
|
+
self.uses: dict[int, set[tuple[Expression, LocationBase | None]]] = {}
|
|
220
254
|
|
|
221
255
|
super().__init__(handlers)
|
|
222
256
|
self.walk(node)
|
|
223
257
|
|
|
224
|
-
def
|
|
225
|
-
"""
|
|
226
|
-
Get unified variable for a given variable.
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
return self._variable_manager.unified_variable(v)
|
|
230
|
-
|
|
231
|
-
def _handle_Statement(self, idx: int, stmt: ailment.Stmt, node: ailment.Block | LoopNode):
|
|
258
|
+
def _handle_Statement(self, idx: int, stmt: Statement, node: ailment.Block | LoopNode):
|
|
232
259
|
if isinstance(stmt, ailment.Stmt.Assignment):
|
|
233
260
|
if is_phi_assignment(stmt):
|
|
234
261
|
return
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
and stmt.dst.was_reg
|
|
238
|
-
and stmt.dst.variable is not None
|
|
239
|
-
):
|
|
240
|
-
u = self._u(stmt.dst.variable)
|
|
241
|
-
if u is not None:
|
|
242
|
-
# dependency
|
|
243
|
-
dependency_finder = ExpressionUseFinder()
|
|
244
|
-
dependency_finder.walk_expression(stmt.src)
|
|
245
|
-
dependencies = tuple({self._u(v) for v in dependency_finder.uses})
|
|
246
|
-
self.assignments[u].add(
|
|
247
|
-
(
|
|
248
|
-
stmt.src,
|
|
249
|
-
dependencies,
|
|
250
|
-
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
251
|
-
dependency_finder.has_load,
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
if (
|
|
255
|
-
isinstance(stmt, ailment.Stmt.Call)
|
|
256
|
-
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
257
|
-
and stmt.ret_expr.was_reg
|
|
258
|
-
and stmt.ret_expr.variable is not None
|
|
259
|
-
):
|
|
260
|
-
u = self._u(stmt.ret_expr.variable)
|
|
261
|
-
if u is not None:
|
|
262
|
+
if isinstance(stmt.dst, ailment.Expr.VirtualVariable) and stmt.dst.was_reg:
|
|
263
|
+
# dependency
|
|
262
264
|
dependency_finder = ExpressionUseFinder()
|
|
263
|
-
dependency_finder.walk_expression(stmt)
|
|
264
|
-
dependencies = tuple(
|
|
265
|
-
self.assignments[
|
|
265
|
+
dependency_finder.walk_expression(stmt.src)
|
|
266
|
+
dependencies = tuple(dependency_finder.uses)
|
|
267
|
+
self.assignments[stmt.dst.varid].add(
|
|
266
268
|
(
|
|
267
|
-
stmt,
|
|
269
|
+
stmt.src,
|
|
268
270
|
dependencies,
|
|
269
271
|
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
270
272
|
dependency_finder.has_load,
|
|
271
273
|
)
|
|
272
274
|
)
|
|
275
|
+
if (
|
|
276
|
+
isinstance(stmt, ailment.Stmt.Call)
|
|
277
|
+
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
278
|
+
and stmt.ret_expr.was_reg
|
|
279
|
+
):
|
|
280
|
+
dependency_finder = ExpressionUseFinder()
|
|
281
|
+
dependency_finder.walk_expression(stmt)
|
|
282
|
+
dependencies = tuple(dependency_finder.uses)
|
|
283
|
+
self.assignments[stmt.ret_expr.varid].add(
|
|
284
|
+
(
|
|
285
|
+
stmt,
|
|
286
|
+
dependencies,
|
|
287
|
+
StatementLocation(node.addr, node.idx if isinstance(node, ailment.Block) else None, idx),
|
|
288
|
+
dependency_finder.has_load,
|
|
289
|
+
)
|
|
290
|
+
)
|
|
273
291
|
|
|
274
292
|
def _handle_Block(self, node: ailment.Block, **kwargs):
|
|
275
|
-
# find assignments
|
|
293
|
+
# find assignments and uses of variables
|
|
294
|
+
use_finder = ExpressionUseFinder()
|
|
276
295
|
for idx, stmt in enumerate(node.statements):
|
|
277
296
|
self._handle_Statement(idx, stmt, node)
|
|
297
|
+
use_finder.walk_statement(stmt, block=node)
|
|
278
298
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
for v, content in use_finder.uses.items():
|
|
284
|
-
u = self._u(v)
|
|
285
|
-
if u is not None:
|
|
286
|
-
if u not in self.uses:
|
|
287
|
-
self.uses[u] = set()
|
|
288
|
-
self.uses[u] |= content
|
|
299
|
+
for varid, content in use_finder.uses.items():
|
|
300
|
+
if varid not in self.uses:
|
|
301
|
+
self.uses[varid] = set()
|
|
302
|
+
self.uses[varid] |= content
|
|
289
303
|
|
|
290
|
-
def _collect_assignments(self, expr:
|
|
304
|
+
def _collect_assignments(self, expr: Expression, node) -> None:
|
|
291
305
|
finder = MultiStatementExpressionAssignmentFinder(self._handle_Statement)
|
|
292
306
|
finder.walk_expression(expr, None, None, node)
|
|
293
307
|
|
|
294
|
-
def _collect_uses(self, expr: Expression, loc: LocationBase):
|
|
308
|
+
def _collect_uses(self, expr: Expression | Statement, loc: LocationBase):
|
|
295
309
|
use_finder = ExpressionUseFinder()
|
|
296
|
-
|
|
310
|
+
if isinstance(expr, Statement):
|
|
311
|
+
use_finder.walk_statement(expr)
|
|
312
|
+
else:
|
|
313
|
+
use_finder.walk_expression(expr, stmt_idx=-1)
|
|
297
314
|
|
|
298
|
-
for
|
|
299
|
-
u = self._u(var)
|
|
300
|
-
if u is None:
|
|
301
|
-
continue
|
|
315
|
+
for varid, uses in use_finder.uses.items():
|
|
302
316
|
for use in uses:
|
|
303
|
-
if
|
|
304
|
-
self.uses[
|
|
305
|
-
self.uses[
|
|
317
|
+
if varid not in self.uses:
|
|
318
|
+
self.uses[varid] = set()
|
|
319
|
+
self.uses[varid].add((use[0], loc))
|
|
306
320
|
|
|
307
321
|
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs):
|
|
308
322
|
# collect uses on the condition expression
|
|
@@ -338,22 +352,185 @@ class ExpressionCounter(SequenceWalker):
|
|
|
338
352
|
return super()._handle_SwitchCase(node, **kwargs)
|
|
339
353
|
|
|
340
354
|
|
|
341
|
-
class
|
|
342
|
-
|
|
355
|
+
class ExpressionSpotter(VVarUsesCollector):
|
|
356
|
+
"""
|
|
357
|
+
ExpressionSpotter collects uses of vvars and existence of Call expressions.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
def __init__(self):
|
|
343
361
|
super().__init__()
|
|
362
|
+
self.has_calls: bool = False
|
|
363
|
+
self.has_loads: bool = False
|
|
364
|
+
|
|
365
|
+
def _handle_CallExpr(self, expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block: Block | None):
|
|
366
|
+
self.has_calls = True
|
|
367
|
+
return super()._handle_CallExpr(expr_idx, expr, stmt_idx, stmt, block)
|
|
368
|
+
|
|
369
|
+
def _handle_Load(self, expr_idx: int, expr: Load, stmt_idx: int, stmt: Statement, block: Block | None):
|
|
370
|
+
self.has_loads = True
|
|
371
|
+
return super()._handle_Load(expr_idx, expr, stmt_idx, stmt, block)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class InterferenceChecker(SequenceWalker):
|
|
375
|
+
"""
|
|
376
|
+
Detect for every pair of definition (assignment) - use if there is anything that may interfere with the definition.
|
|
377
|
+
|
|
378
|
+
Interferences may be caused by:
|
|
379
|
+
|
|
380
|
+
- another call
|
|
381
|
+
- function return
|
|
382
|
+
- store statements
|
|
383
|
+
- load expressions
|
|
384
|
+
- Condition and CascadingCondition nodes
|
|
385
|
+
"""
|
|
386
|
+
|
|
387
|
+
def __init__(self, assignments: dict[int, Any], uses: dict[int, Any], node):
|
|
388
|
+
handlers = {
|
|
389
|
+
ailment.Block: self._handle_Block,
|
|
390
|
+
ConditionNode: self._handle_Condition,
|
|
391
|
+
ConditionalBreakNode: self._handle_ConditionalBreak,
|
|
392
|
+
SwitchCaseNode: self._handle_SwitchCase,
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
super().__init__(handlers, update_seqnode_in_place=False, force_forward_scan=True)
|
|
344
396
|
self._assignments = assignments
|
|
345
397
|
self._uses = uses
|
|
346
|
-
self.
|
|
398
|
+
self._assignment_interferences: dict[int, list] = {}
|
|
399
|
+
self.interfered_assignments: set[int] = set()
|
|
400
|
+
self.walk(node)
|
|
347
401
|
|
|
348
|
-
def
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
402
|
+
def _after_spotting(self, obj, spotter: ExpressionSpotter) -> None:
|
|
403
|
+
if spotter.has_calls or spotter.has_loads:
|
|
404
|
+
# mark all existing assignments as interfered
|
|
405
|
+
for vid in list(self._assignment_interferences):
|
|
406
|
+
self._assignment_interferences[vid].append(obj)
|
|
407
|
+
if set(self._assignment_interferences).intersection(spotter.vvars):
|
|
408
|
+
for used_vvar_id in spotter.vvars:
|
|
409
|
+
if used_vvar_id in self._assignment_interferences:
|
|
410
|
+
if self._assignment_interferences[used_vvar_id]:
|
|
411
|
+
self.interfered_assignments.add(used_vvar_id)
|
|
412
|
+
del self._assignment_interferences[used_vvar_id]
|
|
353
413
|
|
|
354
|
-
def
|
|
414
|
+
def _handle_Block(self, node: ailment.Block, **kwargs):
|
|
415
|
+
for stmt in node.statements:
|
|
416
|
+
|
|
417
|
+
# deal with uses
|
|
418
|
+
spotter = ExpressionSpotter()
|
|
419
|
+
# special case: we process the call arguments first, then the call itself. this is to allow more expression
|
|
420
|
+
# folding opportunities.
|
|
421
|
+
the_call = None
|
|
422
|
+
if isinstance(stmt, Assignment) and isinstance(stmt.src, ailment.Stmt.Call):
|
|
423
|
+
the_call = stmt.src
|
|
424
|
+
elif isinstance(stmt, ailment.Stmt.Call) and not isinstance(stmt.target, str):
|
|
425
|
+
the_call = stmt
|
|
426
|
+
if the_call is not None:
|
|
427
|
+
spotter.walk_expression(the_call.target)
|
|
428
|
+
if the_call.args:
|
|
429
|
+
for arg in the_call.args:
|
|
430
|
+
spotter.walk_expression(arg)
|
|
431
|
+
self._after_spotting(the_call, spotter)
|
|
432
|
+
spotter.walk_statement(stmt)
|
|
433
|
+
self._after_spotting(stmt, spotter)
|
|
434
|
+
|
|
435
|
+
if isinstance(stmt, Return):
|
|
436
|
+
# mark all existing assignments as interfered
|
|
437
|
+
for vid in self._assignment_interferences:
|
|
438
|
+
self._assignment_interferences[vid].append(stmt)
|
|
439
|
+
|
|
440
|
+
if isinstance(stmt, ailment.Stmt.Store):
|
|
441
|
+
# mark all existing assignments as interfered
|
|
442
|
+
for vid in self._assignment_interferences:
|
|
443
|
+
self._assignment_interferences[vid].append(stmt)
|
|
444
|
+
|
|
445
|
+
if isinstance(stmt, ailment.Stmt.Call):
|
|
446
|
+
# mark all existing assignments as interfered
|
|
447
|
+
for vid in self._assignment_interferences:
|
|
448
|
+
self._assignment_interferences[vid].append(stmt)
|
|
449
|
+
|
|
450
|
+
# deal with defs
|
|
451
|
+
if (
|
|
452
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
453
|
+
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
454
|
+
and stmt.dst.was_reg
|
|
455
|
+
and stmt.dst.varid in self._assignments
|
|
456
|
+
):
|
|
457
|
+
# we found this def
|
|
458
|
+
self._assignment_interferences[stmt.dst.varid] = []
|
|
459
|
+
|
|
460
|
+
if (
|
|
461
|
+
isinstance(stmt, ailment.Stmt.Call)
|
|
462
|
+
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
463
|
+
and stmt.ret_expr.was_reg
|
|
464
|
+
and stmt.ret_expr.variable is not None
|
|
465
|
+
and stmt.ret_expr.varid in self._assignments
|
|
466
|
+
):
|
|
467
|
+
# we found this def
|
|
468
|
+
self._assignment_interferences[stmt.ret_expr.varid] = []
|
|
469
|
+
|
|
470
|
+
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs):
|
|
471
|
+
spotter = ExpressionSpotter()
|
|
472
|
+
spotter.walk_expression(node.condition)
|
|
473
|
+
self._after_spotting(node, spotter)
|
|
474
|
+
return super()._handle_ConditionalBreak(node, **kwargs)
|
|
475
|
+
|
|
476
|
+
def _handle_Condition(self, node: ConditionNode, **kwargs):
|
|
477
|
+
spotter = ExpressionSpotter()
|
|
478
|
+
spotter.walk_expression(node.condition)
|
|
479
|
+
self._after_spotting(node, spotter)
|
|
480
|
+
|
|
481
|
+
# mark all existing assignments as interfered
|
|
482
|
+
for vid in self._assignment_interferences:
|
|
483
|
+
self._assignment_interferences[vid].append(node)
|
|
484
|
+
|
|
485
|
+
return super()._handle_Condition(node, **kwargs)
|
|
486
|
+
|
|
487
|
+
def _handle_CascadingCondition(self, node: CascadingConditionNode, **kwargs):
|
|
488
|
+
spotter = ExpressionSpotter()
|
|
489
|
+
for cond, _ in node.condition_and_nodes: # pylint:disable=consider-using-enumerate
|
|
490
|
+
spotter.walk_expression(cond)
|
|
491
|
+
self._after_spotting(node, spotter)
|
|
492
|
+
|
|
493
|
+
# mark all existing assignments as interfered
|
|
494
|
+
for vid in self._assignment_interferences:
|
|
495
|
+
self._assignment_interferences[vid].append(node)
|
|
496
|
+
|
|
497
|
+
return super()._handle_CascadingCondition(node, **kwargs)
|
|
498
|
+
|
|
499
|
+
def _handle_Loop(self, node: LoopNode, **kwargs):
|
|
500
|
+
spotter = ExpressionSpotter()
|
|
501
|
+
|
|
502
|
+
# iterator
|
|
503
|
+
if node.iterator is not None:
|
|
504
|
+
spotter.walk_statement(node.iterator)
|
|
505
|
+
|
|
506
|
+
# initializer
|
|
507
|
+
if node.initializer is not None:
|
|
508
|
+
spotter.walk_statement(node.initializer)
|
|
509
|
+
|
|
510
|
+
# condition
|
|
511
|
+
if node.condition is not None:
|
|
512
|
+
spotter.walk_expression(node.condition)
|
|
513
|
+
|
|
514
|
+
self._after_spotting(node, spotter)
|
|
515
|
+
|
|
516
|
+
return super()._handle_Loop(node, **kwargs)
|
|
517
|
+
|
|
518
|
+
def _handle_SwitchCase(self, node: SwitchCaseNode, **kwargs):
|
|
519
|
+
spotter = ExpressionSpotter()
|
|
520
|
+
spotter.walk_expression(node.switch_expr)
|
|
521
|
+
self._after_spotting(node, spotter)
|
|
522
|
+
return super()._handle_SwitchCase(node, **kwargs)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class ExpressionReplacer(AILBlockWalker):
|
|
526
|
+
def __init__(self, assignments: dict[int, Any], uses: dict[int, Any]):
|
|
527
|
+
super().__init__()
|
|
528
|
+
self._assignments = assignments
|
|
529
|
+
self._uses = uses
|
|
530
|
+
|
|
531
|
+
def _handle_MultiStatementExpression( # type: ignore
|
|
355
532
|
self, expr_idx, expr: MultiStatementExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
356
|
-
):
|
|
533
|
+
) -> Expression | None:
|
|
357
534
|
changed = False
|
|
358
535
|
new_statements = []
|
|
359
536
|
for idx, stmt_ in enumerate(expr.stmts):
|
|
@@ -386,7 +563,7 @@ class ExpressionReplacer(AILBlockWalker):
|
|
|
386
563
|
if changed:
|
|
387
564
|
if not new_statements:
|
|
388
565
|
# it is no longer a multi-statement expression
|
|
389
|
-
return new_expr
|
|
566
|
+
return new_expr # type: ignore
|
|
390
567
|
expr_ = expr.copy()
|
|
391
568
|
expr_.expr = new_expr
|
|
392
569
|
expr_.stmts = new_statements
|
|
@@ -424,16 +601,14 @@ class ExpressionReplacer(AILBlockWalker):
|
|
|
424
601
|
def _handle_expr(
|
|
425
602
|
self, expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
|
|
426
603
|
) -> Any:
|
|
427
|
-
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg and expr.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
replace_with, _ = self._assignments[unified_var]
|
|
431
|
-
return replace_with
|
|
604
|
+
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_reg and expr.varid in self._uses:
|
|
605
|
+
replace_with, _ = self._assignments[expr.varid]
|
|
606
|
+
return replace_with
|
|
432
607
|
return super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
|
|
433
608
|
|
|
434
609
|
|
|
435
610
|
class ExpressionFolder(SequenceWalker):
|
|
436
|
-
def __init__(self, assignments: dict, uses: dict,
|
|
611
|
+
def __init__(self, assignments: dict[int, Any], uses: dict[int, Any], node):
|
|
437
612
|
handlers = {
|
|
438
613
|
ailment.Block: self._handle_Block,
|
|
439
614
|
ConditionNode: self._handle_Condition,
|
|
@@ -444,15 +619,8 @@ class ExpressionFolder(SequenceWalker):
|
|
|
444
619
|
super().__init__(handlers)
|
|
445
620
|
self._assignments = assignments
|
|
446
621
|
self._uses = uses
|
|
447
|
-
self._variable_manager = variable_manager
|
|
448
622
|
self.walk(node)
|
|
449
623
|
|
|
450
|
-
def _u(self, v) -> SimVariable | None:
|
|
451
|
-
"""
|
|
452
|
-
Get unified variable for a given variable.
|
|
453
|
-
"""
|
|
454
|
-
return self._variable_manager.unified_variable(v)
|
|
455
|
-
|
|
456
624
|
def _handle_Block(self, node: ailment.Block, **kwargs):
|
|
457
625
|
# Walk the block to remove each assignment and replace uses of each variable
|
|
458
626
|
new_stmts = []
|
|
@@ -461,45 +629,42 @@ class ExpressionFolder(SequenceWalker):
|
|
|
461
629
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
462
630
|
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
463
631
|
and stmt.dst.was_reg
|
|
464
|
-
and stmt.dst.
|
|
632
|
+
and stmt.dst.varid in self._assignments
|
|
465
633
|
):
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
# remove this statement
|
|
469
|
-
continue
|
|
634
|
+
# remove this statement
|
|
635
|
+
continue
|
|
470
636
|
if (
|
|
471
637
|
isinstance(stmt, ailment.Stmt.Call)
|
|
472
638
|
and isinstance(stmt.ret_expr, ailment.Expr.VirtualVariable)
|
|
473
639
|
and stmt.ret_expr.was_reg
|
|
474
640
|
and stmt.ret_expr.variable is not None
|
|
641
|
+
and stmt.ret_expr.varid in self._assignments
|
|
475
642
|
):
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
# remove this statement
|
|
479
|
-
continue
|
|
643
|
+
# remove this statement
|
|
644
|
+
continue
|
|
480
645
|
new_stmts.append(stmt)
|
|
481
646
|
node.statements = new_stmts
|
|
482
647
|
|
|
483
648
|
# Walk the block to replace the use of each variable
|
|
484
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
649
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
485
650
|
replacer.walk(node)
|
|
486
651
|
|
|
487
652
|
def _handle_ConditionalBreak(self, node: ConditionalBreakNode, **kwargs):
|
|
488
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
653
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
489
654
|
r = replacer.walk_expression(node.condition)
|
|
490
655
|
if r is not None and r is not node.condition:
|
|
491
656
|
node.condition = r
|
|
492
657
|
return super()._handle_ConditionalBreak(node, **kwargs)
|
|
493
658
|
|
|
494
659
|
def _handle_Condition(self, node: ConditionNode, **kwargs):
|
|
495
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
660
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
496
661
|
r = replacer.walk_expression(node.condition)
|
|
497
662
|
if r is not None and r is not node.condition:
|
|
498
663
|
node.condition = r
|
|
499
664
|
return super()._handle_Condition(node, **kwargs)
|
|
500
665
|
|
|
501
666
|
def _handle_CascadingCondition(self, node: CascadingConditionNode, **kwargs):
|
|
502
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
667
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
503
668
|
for idx in range(len(node.condition_and_nodes)): # pylint:disable=consider-using-enumerate
|
|
504
669
|
cond, _ = node.condition_and_nodes[idx]
|
|
505
670
|
r = replacer.walk_expression(cond)
|
|
@@ -508,17 +673,17 @@ class ExpressionFolder(SequenceWalker):
|
|
|
508
673
|
return super()._handle_CascadingCondition(node, **kwargs)
|
|
509
674
|
|
|
510
675
|
def _handle_Loop(self, node: LoopNode, **kwargs):
|
|
511
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
676
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
512
677
|
|
|
513
678
|
# iterator
|
|
514
679
|
if node.iterator is not None:
|
|
515
|
-
r = replacer.
|
|
680
|
+
r = replacer.walk_statement(node.iterator)
|
|
516
681
|
if r is not None and r is not node.iterator:
|
|
517
682
|
node.iterator = r
|
|
518
683
|
|
|
519
684
|
# initializer
|
|
520
685
|
if node.initializer is not None:
|
|
521
|
-
r = replacer.
|
|
686
|
+
r = replacer.walk_statement(node.initializer)
|
|
522
687
|
if r is not None and r is not node.initializer:
|
|
523
688
|
node.initializer = r
|
|
524
689
|
|
|
@@ -531,7 +696,7 @@ class ExpressionFolder(SequenceWalker):
|
|
|
531
696
|
return super()._handle_Loop(node, **kwargs)
|
|
532
697
|
|
|
533
698
|
def _handle_SwitchCase(self, node: SwitchCaseNode, **kwargs):
|
|
534
|
-
replacer = ExpressionReplacer(self._assignments, self._uses
|
|
699
|
+
replacer = ExpressionReplacer(self._assignments, self._uses)
|
|
535
700
|
|
|
536
701
|
r = replacer.walk_expression(node.switch_expr)
|
|
537
702
|
if r is not None and r is not node.switch_expr:
|