angr 9.2.139__py3-none-manylinux2014_aarch64.whl → 9.2.141__py3-none-manylinux2014_aarch64.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.

Files changed (87) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +136 -53
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +11 -0
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
  8. angr/analyses/decompiler/ail_simplifier.py +115 -72
  9. angr/analyses/decompiler/callsite_maker.py +24 -11
  10. angr/analyses/decompiler/clinic.py +78 -43
  11. angr/analyses/decompiler/decompiler.py +18 -7
  12. angr/analyses/decompiler/expression_narrower.py +1 -1
  13. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  14. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  15. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  16. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  17. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
  18. angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
  19. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
  20. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  21. angr/analyses/decompiler/region_identifier.py +6 -4
  22. angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
  23. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
  24. angr/analyses/decompiler/ssailification/rewriting.py +23 -15
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
  26. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  27. angr/analyses/decompiler/structured_codegen/c.py +73 -137
  28. angr/analyses/decompiler/structuring/dream.py +22 -18
  29. angr/analyses/decompiler/structuring/phoenix.py +158 -41
  30. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  31. angr/analyses/decompiler/structuring/structurer_base.py +37 -10
  32. angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
  33. angr/analyses/decompiler/utils.py +106 -21
  34. angr/analyses/deobfuscator/api_obf_finder.py +8 -5
  35. angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
  36. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  37. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  38. angr/analyses/propagator/top_checker_mixin.py +6 -6
  39. angr/analyses/reaching_definitions/__init__.py +2 -1
  40. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  41. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  42. angr/analyses/reaching_definitions/function_handler.py +15 -2
  43. angr/analyses/reaching_definitions/rd_state.py +1 -37
  44. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  45. angr/analyses/s_propagator.py +6 -41
  46. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  47. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  48. angr/analyses/stack_pointer_tracker.py +36 -22
  49. angr/analyses/typehoon/simple_solver.py +45 -7
  50. angr/analyses/typehoon/typeconsts.py +18 -5
  51. angr/analyses/variable_recovery/engine_ail.py +1 -1
  52. angr/analyses/variable_recovery/engine_base.py +7 -5
  53. angr/analyses/variable_recovery/engine_vex.py +20 -4
  54. angr/block.py +69 -107
  55. angr/callable.py +14 -7
  56. angr/calling_conventions.py +30 -11
  57. angr/distributed/__init__.py +1 -1
  58. angr/engines/__init__.py +7 -8
  59. angr/engines/engine.py +1 -120
  60. angr/engines/failure.py +2 -2
  61. angr/engines/hook.py +2 -2
  62. angr/engines/light/engine.py +2 -2
  63. angr/engines/pcode/engine.py +2 -14
  64. angr/engines/procedure.py +2 -2
  65. angr/engines/soot/engine.py +2 -2
  66. angr/engines/soot/statements/switch.py +1 -1
  67. angr/engines/successors.py +124 -11
  68. angr/engines/syscall.py +2 -2
  69. angr/engines/unicorn.py +3 -3
  70. angr/engines/vex/heavy/heavy.py +3 -15
  71. angr/factory.py +12 -22
  72. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  73. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  74. angr/knowledge_plugins/variables/variable_manager.py +7 -5
  75. angr/sim_type.py +19 -17
  76. angr/simos/simos.py +3 -1
  77. angr/state_plugins/plugin.py +19 -4
  78. angr/storage/memory_mixins/memory_mixin.py +1 -1
  79. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  80. angr/utils/ssa/__init__.py +119 -4
  81. angr/utils/types.py +48 -0
  82. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
  83. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
  84. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
  85. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
  86. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
  87. {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 f"Loc: Expression@{self.block_addr:x}.{self.block_idx}-{self.stmt_idx}[{self.expr_idx}]"
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[SimVariable, set[tuple[Expression, ExpressionLocation | None]]] = defaultdict(set)
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 and expr.variable is not None:
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.variable].add((expr, ExpressionLocation(block.addr, block.idx, stmt_idx, expr_idx)))
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.variable].add((expr, None))
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, variable_manager):
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[SimVariable, set[tuple[Expression, LocationBase | None]]] = {}
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 _u(self, v) -> SimVariable | None:
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
- isinstance(stmt.dst, ailment.Expr.VirtualVariable)
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({self._u(v) for v in dependency_finder.uses})
265
- self.assignments[u].add(
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
- # walk the block and find uses of variables
280
- use_finder = ExpressionUseFinder()
281
- use_finder.walk(node)
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: ailment.Expr, node) -> None:
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
- use_finder.walk_expression(expr, stmt_idx=-1)
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 var, uses in use_finder.uses.items():
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 u not in self.uses:
304
- self.uses[u] = set()
305
- self.uses[u].add((use[0], loc))
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 ExpressionReplacer(AILBlockWalker):
342
- def __init__(self, assignments: dict, uses: dict, variable_manager):
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._variable_manager: VariableManagerInternal = variable_manager
398
+ self._assignment_interferences: dict[int, list] = {}
399
+ self.interfered_assignments: set[int] = set()
400
+ self.walk(node)
347
401
 
348
- def _u(self, v) -> SimVariable | None:
349
- """
350
- Get unified variable for a given variable.
351
- """
352
- return self._variable_manager.unified_variable(v)
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 _handle_MultiStatementExpression(
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.variable is not None:
428
- unified_var = self._u(expr.variable)
429
- if unified_var in self._uses:
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, node, variable_manager):
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.variable is not None
632
+ and stmt.dst.varid in self._assignments
465
633
  ):
466
- unified_var = self._u(stmt.dst.variable)
467
- if unified_var in self._assignments:
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
- unified_var = self._u(stmt.ret_expr.variable)
477
- if unified_var in self._assignments:
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, self._variable_manager)
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, self._variable_manager)
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, self._variable_manager)
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, self._variable_manager)
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, self._variable_manager)
676
+ replacer = ExpressionReplacer(self._assignments, self._uses)
512
677
 
513
678
  # iterator
514
679
  if node.iterator is not None:
515
- r = replacer.walk_expression(node.iterator)
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.walk_expression(node.initializer)
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, self._variable_manager)
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: