angr 9.2.142__py3-none-manylinux2014_aarch64.whl → 9.2.144__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 (61) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +22 -10
  3. angr/analyses/calling_convention/fact_collector.py +72 -14
  4. angr/analyses/cfg/cfg_base.py +7 -2
  5. angr/analyses/cfg/cfg_emulated.py +13 -4
  6. angr/analyses/cfg/cfg_fast.py +21 -60
  7. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
  8. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +12 -1
  9. angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
  10. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +2 -1
  11. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +6 -102
  12. angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
  13. angr/analyses/complete_calling_conventions.py +18 -5
  14. angr/analyses/decompiler/ail_simplifier.py +95 -65
  15. angr/analyses/decompiler/clinic.py +162 -68
  16. angr/analyses/decompiler/decompiler.py +4 -4
  17. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +1 -1
  18. angr/analyses/decompiler/optimization_passes/condition_constprop.py +49 -14
  19. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +8 -0
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -5
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +5 -0
  22. angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
  23. angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
  24. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +15 -1
  25. angr/analyses/decompiler/sequence_walker.py +8 -0
  26. angr/analyses/decompiler/ssailification/rewriting_engine.py +2 -0
  27. angr/analyses/decompiler/ssailification/ssailification.py +10 -2
  28. angr/analyses/decompiler/ssailification/traversal_engine.py +17 -2
  29. angr/analyses/decompiler/structured_codegen/c.py +25 -4
  30. angr/analyses/decompiler/utils.py +13 -0
  31. angr/analyses/disassembly.py +3 -3
  32. angr/analyses/fcp/fcp.py +1 -4
  33. angr/analyses/s_propagator.py +40 -29
  34. angr/analyses/s_reaching_definitions/s_rda_model.py +45 -36
  35. angr/analyses/s_reaching_definitions/s_rda_view.py +6 -3
  36. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +41 -42
  37. angr/analyses/typehoon/dfa.py +13 -3
  38. angr/analyses/typehoon/typehoon.py +60 -18
  39. angr/analyses/typehoon/typevars.py +11 -7
  40. angr/analyses/variable_recovery/engine_ail.py +19 -23
  41. angr/analyses/variable_recovery/engine_base.py +26 -30
  42. angr/analyses/variable_recovery/variable_recovery_fast.py +17 -21
  43. angr/calling_conventions.py +18 -8
  44. angr/knowledge_plugins/functions/function.py +29 -15
  45. angr/knowledge_plugins/key_definitions/constants.py +2 -2
  46. angr/knowledge_plugins/key_definitions/liveness.py +4 -4
  47. angr/lib/angr_native.so +0 -0
  48. angr/procedures/definitions/linux_kernel.py +5 -0
  49. angr/state_plugins/unicorn_engine.py +24 -8
  50. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -2
  51. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +2 -2
  52. angr/utils/doms.py +40 -33
  53. angr/utils/graph.py +26 -20
  54. angr/utils/ssa/__init__.py +21 -14
  55. angr/utils/ssa/vvar_uses_collector.py +2 -2
  56. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/METADATA +11 -8
  57. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/RECORD +61 -58
  58. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/WHEEL +1 -1
  59. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/LICENSE +0 -0
  60. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/entry_points.txt +0 -0
  61. {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/top_level.txt +0 -0
@@ -36,6 +36,7 @@ from angr.knowledge_plugins.key_definitions.definition import Definition
36
36
  from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
37
37
  from angr.errors import AngrRuntimeError
38
38
  from angr.analyses import Analysis, AnalysesHub
39
+ from angr.utils.timing import timethis
39
40
  from .ailgraph_walker import AILGraphWalker
40
41
  from .expression_narrower import ExprNarrowingInfo, NarrowingInfoExtractor, ExpressionNarrower
41
42
  from .block_simplifier import BlockSimplifier
@@ -202,6 +203,7 @@ class AILSimplifier(Analysis):
202
203
  AILGraphWalker(self.func_graph, _handler, replace_nodes=True).walk()
203
204
  self.blocks = {}
204
205
 
206
+ @timethis
205
207
  def _compute_reaching_definitions(self) -> SRDAModel:
206
208
  # Computing reaching definitions or return the cached one
207
209
  if self._reaching_definitions is not None:
@@ -217,6 +219,7 @@ class AILSimplifier(Analysis):
217
219
  self._reaching_definitions = rd
218
220
  return rd
219
221
 
222
+ @timethis
220
223
  def _compute_propagation(self) -> SPropagatorAnalysis:
221
224
  # Propagate expressions or return the existing result
222
225
  if self._propagator is not None:
@@ -233,6 +236,7 @@ class AILSimplifier(Analysis):
233
236
  self._propagator_dead_vvar_ids = prop.dead_vvar_ids
234
237
  return prop
235
238
 
239
+ @timethis
236
240
  def _compute_equivalence(self) -> set[Equivalence]:
237
241
  equivalence = set()
238
242
  for block in self.func_graph:
@@ -281,6 +285,7 @@ class AILSimplifier(Analysis):
281
285
  # Expression narrowing
282
286
  #
283
287
 
288
+ @timethis
284
289
  def _narrow_exprs(self) -> bool:
285
290
  """
286
291
  A register may be used with full width even when only the lower bytes are really needed. This results in the
@@ -511,9 +516,9 @@ class AILSimplifier(Analysis):
511
516
  atom = atom_queue.pop(0)
512
517
  seen.add(atom)
513
518
 
514
- use_and_exprs = rd.get_vvar_uses_with_expr(atom)
519
+ expr_and_uses = rd.all_vvar_uses[atom.varid]
515
520
 
516
- for loc, expr in use_and_exprs:
521
+ for expr, loc in set(expr_and_uses):
517
522
  old_block = block_dict.get((loc.block_addr, loc.block_idx), None)
518
523
  if old_block is None:
519
524
  # missing a block for whatever reason
@@ -532,6 +537,7 @@ class AILSimplifier(Analysis):
532
537
  )
533
538
  if new_atom not in seen:
534
539
  atom_queue.append(new_atom)
540
+ seen.add(new_atom)
535
541
  else:
536
542
  result.append((atom, loc, expr))
537
543
  return result, phi_vars
@@ -659,6 +665,7 @@ class AILSimplifier(Analysis):
659
665
  # Unifying local variables
660
666
  #
661
667
 
668
+ @timethis
662
669
  def _unify_local_variables(self) -> bool:
663
670
  """
664
671
  Find variables that are definitely equivalent and then eliminate unnecessary copies.
@@ -822,14 +829,14 @@ class AILSimplifier(Analysis):
822
829
  continue
823
830
 
824
831
  # find all its uses
825
- all_arg_copy_var_uses: set[tuple[CodeLocation, Any]] = set(
826
- rd.get_vvar_uses_with_expr(arg_copy_def.atom)
832
+ all_arg_copy_var_uses: set[tuple[Any, CodeLocation]] = rd.get_vvar_uses_with_expr(
833
+ arg_copy_def.atom
827
834
  )
828
835
  all_uses_with_def = set()
829
836
 
830
837
  should_abort = False
831
838
  for use in all_arg_copy_var_uses:
832
- used_expr = use[1]
839
+ used_expr = use[0]
833
840
  if used_expr is not None and used_expr.size != arg_copy_def.size:
834
841
  should_abort = True
835
842
  break
@@ -924,15 +931,19 @@ class AILSimplifier(Analysis):
924
931
 
925
932
  # find all uses of this definition
926
933
  # we make a copy of the set since we may touch the set (uses) when replacing expressions
927
- all_uses: set[tuple[CodeLocation, Any]] = set(rd.get_vvar_uses_with_expr(to_replace_def.atom))
934
+ all_uses: set[tuple[Any, CodeLocation]] = set(rd.all_vvar_uses[to_replace_def.atom.varid])
928
935
  # make sure none of these uses are phi nodes (depends on more than one def)
929
936
  all_uses_with_unique_def = set()
930
- for use_and_expr in all_uses:
931
- use_loc, used_expr = use_and_expr
937
+ for expr_and_use in all_uses:
938
+ used_expr, use_loc = expr_and_use
932
939
  defs_and_exprs = rd.get_uses_by_location(use_loc, exprs=True)
933
- filtered_defs = {def_ for def_, expr_ in defs_and_exprs if expr_ == used_expr}
940
+ filtered_defs = {
941
+ def_
942
+ for def_, expr_ in defs_and_exprs
943
+ if expr_ is not None and used_expr is not None and expr_.varid == used_expr.varid
944
+ }
934
945
  if len(filtered_defs) == 1:
935
- all_uses_with_unique_def.add(use_and_expr)
946
+ all_uses_with_unique_def.add(expr_and_use)
936
947
  else:
937
948
  # optimization: break early
938
949
  break
@@ -947,7 +958,7 @@ class AILSimplifier(Analysis):
947
958
 
948
959
  if not (isinstance(replace_with, VirtualVariable) and replace_with.was_parameter):
949
960
  assignment_ctr = 0
950
- all_use_locs = {use_loc for use_loc, _ in all_uses}
961
+ all_use_locs = {use_loc for _, use_loc in all_uses}
951
962
  for use_loc in all_use_locs:
952
963
  if use_loc == eq.codeloc:
953
964
  continue
@@ -960,17 +971,17 @@ class AILSimplifier(Analysis):
960
971
  if assignment_ctr > 1:
961
972
  continue
962
973
 
963
- all_uses_with_def = {(to_replace_def, use_and_expr) for use_and_expr in all_uses}
974
+ all_uses_with_def = {(to_replace_def, expr_and_use) for expr_and_use in all_uses}
964
975
 
965
976
  remove_initial_assignment = False # expression folding will take care of it
966
977
 
967
978
  assert replace_with is not None
968
979
 
969
- if any(not isinstance(use_and_expr[1], VirtualVariable) for _, use_and_expr in all_uses_with_def):
980
+ if any(not isinstance(expr_and_use[0], VirtualVariable) for _, expr_and_use in all_uses_with_def):
970
981
  # if any of the uses are phi assignments, we skip
971
982
  used_in_phi_assignment = False
972
- for _, use_and_expr in all_uses_with_def:
973
- u = use_and_expr[0]
983
+ for _, expr_and_use in all_uses_with_def:
984
+ u = expr_and_use[1]
974
985
  assert u.block_addr is not None
975
986
  assert u.stmt_idx is not None
976
987
  block = addr_and_idx_to_block[(u.block_addr, u.block_idx)]
@@ -983,8 +994,8 @@ class AILSimplifier(Analysis):
983
994
 
984
995
  # ensure the uses we consider are all after the eq location
985
996
  filtered_all_uses_with_def = []
986
- for def_, use_and_expr in all_uses_with_def:
987
- u = use_and_expr[0]
997
+ for def_, expr_and_use in all_uses_with_def:
998
+ u = expr_and_use[1]
988
999
  if (
989
1000
  u.block_addr == eq.codeloc.block_addr
990
1001
  and u.block_idx == eq.codeloc.block_idx
@@ -992,7 +1003,7 @@ class AILSimplifier(Analysis):
992
1003
  ):
993
1004
  # this use happens before the assignment - ignore it
994
1005
  continue
995
- filtered_all_uses_with_def.append((def_, use_and_expr))
1006
+ filtered_all_uses_with_def.append((def_, expr_and_use))
996
1007
  all_uses_with_def = filtered_all_uses_with_def
997
1008
 
998
1009
  if not all_uses_with_def:
@@ -1004,8 +1015,8 @@ class AILSimplifier(Analysis):
1004
1015
 
1005
1016
  # replace all uses
1006
1017
  all_uses_replaced = True
1007
- for def_, use_and_expr in all_uses_with_def:
1008
- u, used_expr = use_and_expr
1018
+ for def_, expr_and_use in all_uses_with_def:
1019
+ used_expr, u = expr_and_use
1009
1020
 
1010
1021
  use_expr_defns = []
1011
1022
  for d in rd.get_uses_by_location(u):
@@ -1110,6 +1121,7 @@ class AILSimplifier(Analysis):
1110
1121
  walker.walk_statement(stmt)
1111
1122
  return len(walker.temps) > 0
1112
1123
 
1124
+ @timethis
1113
1125
  def _fold_call_exprs(self) -> bool:
1114
1126
  """
1115
1127
  Fold a call expression (statement) into other statements if the return value of the call expression (statement)
@@ -1183,11 +1195,11 @@ class AILSimplifier(Analysis):
1183
1195
  assert the_def.codeloc.block_addr is not None
1184
1196
  assert the_def.codeloc.stmt_idx is not None
1185
1197
 
1186
- all_uses: set[tuple[CodeLocation, Any]] = set(rd.get_vvar_uses_with_expr(the_def.atom))
1198
+ all_uses: set[tuple[Any, CodeLocation]] = rd.get_vvar_uses_with_expr(the_def.atom)
1187
1199
 
1188
1200
  if len(all_uses) != 1:
1189
1201
  continue
1190
- u, used_expr = next(iter(all_uses))
1202
+ used_expr, u = next(iter(all_uses))
1191
1203
  if used_expr is None:
1192
1204
  continue
1193
1205
  assert u.block_addr is not None
@@ -1295,6 +1307,11 @@ class AILSimplifier(Analysis):
1295
1307
  # check its predecessors
1296
1308
  succ_predecessors = list(self.func_graph.predecessors(succ))
1297
1309
  if len(succ_predecessors) == 1:
1310
+ if succ in lst:
1311
+ # we are about to form a loop - bad!
1312
+ # example: binary ce1897b492c80bf94083dd783aefb413ab1f6d8d4981adce8420f6669d0cb3e1, block
1313
+ # 0x2976EF7.
1314
+ break
1298
1315
  lst.append(succ)
1299
1316
  else:
1300
1317
  break
@@ -1314,6 +1331,7 @@ class AILSimplifier(Analysis):
1314
1331
 
1315
1332
  return False, None
1316
1333
 
1334
+ @timethis
1317
1335
  def _iteratively_remove_dead_assignments(self) -> bool:
1318
1336
  anything_removed = False
1319
1337
  while True:
@@ -1323,6 +1341,7 @@ class AILSimplifier(Analysis):
1323
1341
  self._rebuild_func_graph()
1324
1342
  self._clear_cache()
1325
1343
 
1344
+ @timethis
1326
1345
  def _remove_dead_assignments(self) -> bool:
1327
1346
 
1328
1347
  # keeping tracking of statements to remove and statements (as well as dead vvars) to keep allows us to handle
@@ -1330,7 +1349,7 @@ class AILSimplifier(Analysis):
1330
1349
  # value and the floating-point return value.
1331
1350
  stmts_to_remove_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1332
1351
  stmts_to_keep_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1333
- dead_vvar_ids: set[int] = set()
1352
+ dead_vvar_ids: set[int] = self._removed_vvar_ids.copy()
1334
1353
  dead_vvar_codelocs: set[CodeLocation] = set()
1335
1354
  blocks: dict[tuple[int, int | None], Block] = {
1336
1355
  (node.addr, node.idx): self.blocks.get(node, node) for node in self.func_graph.nodes()
@@ -1343,36 +1362,43 @@ class AILSimplifier(Analysis):
1343
1362
  stackarg_offsets = (
1344
1363
  {(tpl[1] & mask) for tpl in self._stack_arg_offsets} if self._stack_arg_offsets is not None else None
1345
1364
  )
1365
+
1346
1366
  while True:
1347
1367
  new_dead_vars_found = False
1348
- for vvar, codeloc in rd.all_vvar_definitions.items():
1349
- if vvar.varid in dead_vvar_ids:
1368
+
1369
+ # traverse all virtual variable definitions
1370
+ for vvar_id, codeloc in rd.all_vvar_definitions.items():
1371
+ if vvar_id in dead_vvar_ids:
1350
1372
  continue
1351
- if vvar.varid in self._propagator_dead_vvar_ids:
1373
+ uses = None
1374
+ if vvar_id in self._propagator_dead_vvar_ids:
1352
1375
  # we are definitely removing this variable if it has no uses
1353
- uses = rd.all_vvar_uses[vvar]
1354
- elif vvar.was_stack:
1355
- if not self._remove_dead_memdefs:
1356
- if rd.is_phi_vvar_id(vvar.varid):
1357
- # we always remove unused phi variables
1358
- pass
1359
- elif vvar.varid in self._secondary_stackvars:
1360
- # secondary stack variables are potentially removable
1361
- pass
1362
- elif stackarg_offsets is not None:
1363
- # we always remove definitions for stack arguments
1364
- assert vvar.stack_offset is not None
1365
- if (vvar.stack_offset & mask) not in stackarg_offsets:
1376
+ uses = rd.all_vvar_uses[vvar_id]
1377
+
1378
+ if uses is None:
1379
+ vvar = rd.varid_to_vvar[vvar_id]
1380
+ if vvar.was_stack:
1381
+ if not self._remove_dead_memdefs:
1382
+ if rd.is_phi_vvar_id(vvar_id):
1383
+ # we always remove unused phi variables
1384
+ pass
1385
+ elif vvar_id in self._secondary_stackvars:
1386
+ # secondary stack variables are potentially removable
1387
+ pass
1388
+ elif stackarg_offsets is not None:
1389
+ # we always remove definitions for stack arguments
1390
+ assert vvar.stack_offset is not None
1391
+ if (vvar.stack_offset & mask) not in stackarg_offsets:
1392
+ continue
1393
+ else:
1366
1394
  continue
1367
- else:
1368
- continue
1369
- uses = rd.all_vvar_uses[vvar]
1395
+ uses = rd.all_vvar_uses[vvar_id]
1370
1396
 
1371
- elif vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
1372
- uses = rd.all_vvar_uses[vvar]
1397
+ elif vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
1398
+ uses = rd.all_vvar_uses[vvar_id]
1373
1399
 
1374
- else:
1375
- uses = set()
1400
+ else:
1401
+ uses = set()
1376
1402
 
1377
1403
  # remove uses where vvars are going to be removed
1378
1404
  filtered_uses_count = 0
@@ -1385,7 +1411,7 @@ class AILSimplifier(Analysis):
1385
1411
 
1386
1412
  if filtered_uses_count == 0:
1387
1413
  new_dead_vars_found = True
1388
- dead_vvar_ids.add(vvar.varid)
1414
+ dead_vvar_ids.add(vvar_id)
1389
1415
  dead_vvar_codelocs.add(codeloc)
1390
1416
  if not isinstance(codeloc, ExternalCodeLocation):
1391
1417
  assert codeloc.block_addr is not None
@@ -1403,30 +1429,29 @@ class AILSimplifier(Analysis):
1403
1429
  break
1404
1430
 
1405
1431
  # find all phi variables that rely on variables that no longer exist
1406
- all_removed_var_ids = self._removed_vvar_ids.copy()
1407
1432
  removed_vvar_ids = self._removed_vvar_ids
1408
1433
  while True:
1409
1434
  new_removed_vvar_ids = set()
1410
1435
  for phi_varid, phi_use_varids in rd.phivarid_to_varids.items():
1411
- if phi_varid not in all_removed_var_ids and any(
1412
- vvarid in removed_vvar_ids for vvarid in phi_use_varids
1413
- ):
1414
- loc = rd.all_vvar_definitions[rd.varid_to_vvar[phi_varid]]
1436
+ if phi_varid not in dead_vvar_ids and any(vvarid in removed_vvar_ids for vvarid in phi_use_varids):
1437
+ loc = rd.all_vvar_definitions[phi_varid]
1415
1438
  assert loc.block_addr is not None and loc.stmt_idx is not None
1416
- stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1417
- new_removed_vvar_ids.add(phi_varid)
1418
- all_removed_var_ids.add(phi_varid)
1439
+ if loc.stmt_idx not in stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)]:
1440
+ stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1441
+ new_removed_vvar_ids.add(phi_varid)
1442
+ dead_vvar_ids.add(phi_varid)
1419
1443
  if not new_removed_vvar_ids:
1420
1444
  break
1421
1445
  removed_vvar_ids = new_removed_vvar_ids
1422
1446
 
1423
1447
  # find all phi variables that are only ever used by other phi variables
1424
- redundant_phi_and_dirty_varids = self._find_cyclic_dependent_phis_and_dirty_vvars(rd)
1448
+ redundant_phi_and_dirty_varids = self._find_cyclic_dependent_phis_and_dirty_vvars(rd, dead_vvar_ids)
1425
1449
  for varid in redundant_phi_and_dirty_varids:
1426
- loc = rd.all_vvar_definitions[rd.varid_to_vvar[varid]]
1450
+ loc = rd.all_vvar_definitions[varid]
1427
1451
  assert loc.block_addr is not None and loc.stmt_idx is not None
1428
- stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1429
- stmts_to_keep_per_block[(loc.block_addr, loc.block_idx)].discard(loc.stmt_idx)
1452
+ if loc.stmt_idx not in stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)]:
1453
+ stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1454
+ stmts_to_keep_per_block[(loc.block_addr, loc.block_idx)].discard(loc.stmt_idx)
1430
1455
 
1431
1456
  for codeloc in self._calls_to_remove | self._assignments_to_remove:
1432
1457
  # this call can be removed. make sure it exists in stmts_to_remove_per_block
@@ -1481,6 +1506,7 @@ class AILSimplifier(Analysis):
1481
1506
  if self._statement_has_call_exprs(stmt):
1482
1507
  if codeloc in self._calls_to_remove:
1483
1508
  # it has a call and must be removed
1509
+ self._calls_to_remove.discard(codeloc)
1484
1510
  simplified = True
1485
1511
  continue
1486
1512
  if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
@@ -1538,9 +1564,8 @@ class AILSimplifier(Analysis):
1538
1564
  :return: The set of vvar use atoms.
1539
1565
  """
1540
1566
 
1541
- vvar = rd.varid_to_vvar[vvar_id]
1542
1567
  used_by: set[int | None] = set()
1543
- for used_vvar, loc in rd.all_vvar_uses[vvar]:
1568
+ for used_vvar, loc in rd.all_vvar_uses[vvar_id]:
1544
1569
  if used_vvar is None:
1545
1570
  # no explicit reference
1546
1571
  used_by.add(None)
@@ -1553,7 +1578,7 @@ class AILSimplifier(Analysis):
1553
1578
  used_by.add(None)
1554
1579
  return used_by
1555
1580
 
1556
- def _find_cyclic_dependent_phis_and_dirty_vvars(self, rd: SRDAModel) -> set[int]:
1581
+ def _find_cyclic_dependent_phis_and_dirty_vvars(self, rd: SRDAModel, dead_vvar_ids: set[int]) -> set[int]:
1557
1582
  blocks_dict: dict[tuple[int, int | None], Block] = {(bb.addr, bb.idx): bb for bb in self.func_graph}
1558
1583
 
1559
1584
  # find dirty vvars and vexccall vvars
@@ -1568,16 +1593,21 @@ class AILSimplifier(Analysis):
1568
1593
  ):
1569
1594
  dirty_vvar_ids.add(stmt.dst.varid)
1570
1595
 
1571
- phi_and_dirty_vvar_ids = rd.phi_vvar_ids | dirty_vvar_ids
1596
+ phi_and_dirty_vvar_ids = (rd.phi_vvar_ids | dirty_vvar_ids).difference(dead_vvar_ids)
1572
1597
 
1573
1598
  vvar_used_by: dict[int, set[int | None]] = defaultdict(set)
1574
1599
  for var_id in phi_and_dirty_vvar_ids:
1575
1600
  if var_id in rd.phivarid_to_varids:
1576
1601
  for used_by_varid in rd.phivarid_to_varids[var_id]:
1602
+ if used_by_varid in dead_vvar_ids:
1603
+ # this variable no longer exists
1604
+ continue
1577
1605
  if used_by_varid not in vvar_used_by:
1578
- vvar_used_by[used_by_varid] |= self._get_vvar_used_by(used_by_varid, rd, blocks_dict)
1606
+ vvar_used_by[used_by_varid] |= self._get_vvar_used_by(
1607
+ used_by_varid, rd, blocks_dict
1608
+ ).difference(dead_vvar_ids)
1579
1609
  vvar_used_by[used_by_varid].add(var_id) # probably unnecessary
1580
- vvar_used_by[var_id] |= self._get_vvar_used_by(var_id, rd, blocks_dict)
1610
+ vvar_used_by[var_id] |= self._get_vvar_used_by(var_id, rd, blocks_dict).difference(dead_vvar_ids)
1581
1611
 
1582
1612
  g = networkx.DiGraph()
1583
1613
  dummy_vvar_id = -1