angr 9.2.102__py3-none-manylinux2014_x86_64.whl → 9.2.104__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.

Files changed (239) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +7 -6
  3. angr/analyses/calling_convention.py +33 -35
  4. angr/analyses/cdg.py +2 -4
  5. angr/analyses/cfg/cfb.py +4 -3
  6. angr/analyses/cfg/cfg_base.py +14 -14
  7. angr/analyses/cfg/cfg_emulated.py +3 -4
  8. angr/analyses/cfg/cfg_fast.py +46 -46
  9. angr/analyses/cfg/cfg_fast_soot.py +1 -2
  10. angr/analyses/cfg/cfg_job_base.py +2 -2
  11. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +14 -13
  12. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +5 -5
  13. angr/analyses/cfg_slice_to_sink/cfg_slice_to_sink.py +3 -3
  14. angr/analyses/complete_calling_conventions.py +13 -12
  15. angr/analyses/data_dep/data_dependency_analysis.py +24 -24
  16. angr/analyses/data_dep/dep_nodes.py +3 -3
  17. angr/analyses/ddg.py +1 -2
  18. angr/analyses/decompiler/ail_simplifier.py +35 -34
  19. angr/analyses/decompiler/block_io_finder.py +20 -20
  20. angr/analyses/decompiler/block_similarity.py +4 -6
  21. angr/analyses/decompiler/block_simplifier.py +17 -16
  22. angr/analyses/decompiler/callsite_maker.py +25 -10
  23. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -3
  24. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +2 -4
  25. angr/analyses/decompiler/clinic.py +250 -45
  26. angr/analyses/decompiler/condition_processor.py +15 -8
  27. angr/analyses/decompiler/decompilation_cache.py +7 -7
  28. angr/analyses/decompiler/decompilation_options.py +4 -4
  29. angr/analyses/decompiler/decompiler.py +19 -15
  30. angr/analyses/decompiler/expression_counters.py +10 -9
  31. angr/analyses/decompiler/goto_manager.py +2 -4
  32. angr/analyses/decompiler/graph_region.py +9 -9
  33. angr/analyses/decompiler/jump_target_collector.py +1 -2
  34. angr/analyses/decompiler/optimization_passes/__init__.py +4 -3
  35. angr/analyses/decompiler/optimization_passes/code_motion.py +5 -6
  36. angr/analyses/decompiler/optimization_passes/const_derefs.py +4 -4
  37. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +73 -0
  38. angr/analyses/decompiler/optimization_passes/engine_base.py +25 -3
  39. angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +6 -5
  40. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
  41. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +3 -0
  42. angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +2 -2
  43. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +17 -17
  44. angr/analyses/decompiler/optimization_passes/optimization_pass.py +12 -13
  45. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +25 -21
  46. angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +3 -3
  47. angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +1 -2
  48. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +7 -7
  49. angr/analyses/decompiler/optimization_passes/spilled_register_finder.py +18 -0
  50. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +2 -3
  51. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +1 -2
  52. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +2 -2
  53. angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -3
  54. angr/analyses/decompiler/peephole_optimizations/base.py +13 -15
  55. angr/analyses/decompiler/peephole_optimizations/bswap.py +1 -3
  56. angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +72 -0
  57. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +1 -2
  58. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +1 -1
  59. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +5 -10
  60. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +3 -4
  61. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +7 -10
  62. angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +2 -3
  63. angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +1 -2
  64. angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +4 -4
  65. angr/analyses/decompiler/redundant_label_remover.py +4 -5
  66. angr/analyses/decompiler/region_identifier.py +4 -5
  67. angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +1 -2
  68. angr/analyses/decompiler/region_simplifiers/expr_folding.py +19 -20
  69. angr/analyses/decompiler/region_simplifiers/goto.py +2 -3
  70. angr/analyses/decompiler/region_simplifiers/loop.py +1 -2
  71. angr/analyses/decompiler/region_simplifiers/node_address_finder.py +1 -2
  72. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +1 -3
  73. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +19 -19
  74. angr/analyses/decompiler/return_maker.py +1 -2
  75. angr/analyses/decompiler/structured_codegen/base.py +5 -6
  76. angr/analyses/decompiler/structured_codegen/c.py +39 -38
  77. angr/analyses/decompiler/structuring/__init__.py +1 -1
  78. angr/analyses/decompiler/structuring/dream.py +17 -16
  79. angr/analyses/decompiler/structuring/phoenix.py +45 -46
  80. angr/analyses/decompiler/structuring/recursive_structurer.py +4 -4
  81. angr/analyses/decompiler/structuring/structurer_base.py +16 -15
  82. angr/analyses/decompiler/structuring/structurer_nodes.py +10 -9
  83. angr/analyses/decompiler/utils.py +17 -16
  84. angr/analyses/disassembly.py +7 -6
  85. angr/analyses/flirt.py +9 -9
  86. angr/analyses/forward_analysis/forward_analysis.py +15 -14
  87. angr/analyses/forward_analysis/visitors/function_graph.py +1 -2
  88. angr/analyses/forward_analysis/visitors/graph.py +16 -15
  89. angr/analyses/propagator/engine_ail.py +30 -26
  90. angr/analyses/propagator/outdated_definition_walker.py +8 -7
  91. angr/analyses/propagator/propagator.py +11 -13
  92. angr/analyses/proximity_graph.py +21 -21
  93. angr/analyses/reaching_definitions/__init__.py +3 -3
  94. angr/analyses/reaching_definitions/call_trace.py +3 -6
  95. angr/analyses/reaching_definitions/dep_graph.py +41 -48
  96. angr/analyses/reaching_definitions/engine_ail.py +11 -5
  97. angr/analyses/reaching_definitions/engine_vex.py +9 -8
  98. angr/analyses/reaching_definitions/function_handler.py +51 -34
  99. angr/analyses/reaching_definitions/heap_allocator.py +3 -4
  100. angr/analyses/reaching_definitions/rd_initializer.py +8 -8
  101. angr/analyses/reaching_definitions/rd_state.py +57 -58
  102. angr/analyses/reaching_definitions/reaching_definitions.py +18 -17
  103. angr/analyses/reaching_definitions/subject.py +2 -3
  104. angr/analyses/stack_pointer_tracker.py +15 -6
  105. angr/analyses/typehoon/dfa.py +4 -4
  106. angr/analyses/typehoon/simple_solver.py +48 -52
  107. angr/analyses/typehoon/translator.py +3 -6
  108. angr/analyses/typehoon/typeconsts.py +13 -14
  109. angr/analyses/typehoon/typehoon.py +9 -9
  110. angr/analyses/typehoon/typevars.py +18 -17
  111. angr/analyses/variable_recovery/engine_ail.py +5 -5
  112. angr/analyses/variable_recovery/engine_base.py +25 -21
  113. angr/analyses/variable_recovery/irsb_scanner.py +8 -9
  114. angr/analyses/variable_recovery/variable_recovery.py +1 -2
  115. angr/analyses/variable_recovery/variable_recovery_base.py +14 -13
  116. angr/analyses/variable_recovery/variable_recovery_fast.py +8 -8
  117. angr/analyses/veritesting.py +1 -2
  118. angr/analyses/vfg.py +57 -56
  119. angr/analyses/xrefs.py +1 -2
  120. angr/angrdb/db.py +7 -7
  121. angr/angrdb/serializers/kb.py +16 -13
  122. angr/angrdb/serializers/loader.py +1 -2
  123. angr/angrdb/serializers/structured_code.py +2 -2
  124. angr/annocfg.py +1 -2
  125. angr/block.py +16 -6
  126. angr/calling_conventions.py +28 -27
  127. angr/code_location.py +8 -8
  128. angr/codenode.py +1 -2
  129. angr/concretization_strategies/max.py +1 -3
  130. angr/distributed/server.py +1 -3
  131. angr/distributed/worker.py +1 -2
  132. angr/engines/engine.py +2 -3
  133. angr/engines/light/engine.py +4 -4
  134. angr/engines/pcode/behavior.py +20 -2
  135. angr/engines/pcode/emulate.py +1 -1
  136. angr/engines/pcode/engine.py +7 -7
  137. angr/engines/pcode/lifter.py +78 -77
  138. angr/engines/vex/claripy/ccall.py +1 -2
  139. angr/engines/vex/claripy/datalayer.py +1 -2
  140. angr/engines/vex/light/light.py +1 -2
  141. angr/exploration_techniques/tracer.py +4 -4
  142. angr/factory.py +12 -15
  143. angr/flirt/__init__.py +8 -8
  144. angr/flirt/build_sig.py +2 -3
  145. angr/keyed_region.py +2 -2
  146. angr/knowledge_base/knowledge_base.py +3 -3
  147. angr/knowledge_plugins/callsite_prototypes.py +4 -6
  148. angr/knowledge_plugins/cfg/cfg_manager.py +19 -6
  149. angr/knowledge_plugins/cfg/cfg_model.py +26 -27
  150. angr/knowledge_plugins/cfg/cfg_node.py +2 -2
  151. angr/knowledge_plugins/cfg/indirect_jump.py +6 -8
  152. angr/knowledge_plugins/cfg/memory_data.py +8 -9
  153. angr/knowledge_plugins/custom_strings.py +1 -3
  154. angr/knowledge_plugins/debug_variables.py +2 -2
  155. angr/knowledge_plugins/functions/function.py +21 -22
  156. angr/knowledge_plugins/functions/function_manager.py +5 -5
  157. angr/knowledge_plugins/indirect_jumps.py +1 -3
  158. angr/knowledge_plugins/key_definitions/atoms.py +7 -7
  159. angr/knowledge_plugins/key_definitions/definition.py +14 -14
  160. angr/knowledge_plugins/key_definitions/environment.py +5 -7
  161. angr/knowledge_plugins/key_definitions/heap_address.py +1 -3
  162. angr/knowledge_plugins/key_definitions/key_definition_manager.py +3 -2
  163. angr/knowledge_plugins/key_definitions/live_definitions.py +60 -59
  164. angr/knowledge_plugins/key_definitions/liveness.py +16 -16
  165. angr/knowledge_plugins/key_definitions/rd_model.py +15 -15
  166. angr/knowledge_plugins/key_definitions/uses.py +11 -11
  167. angr/knowledge_plugins/patches.py +4 -8
  168. angr/knowledge_plugins/propagations/prop_value.py +10 -9
  169. angr/knowledge_plugins/propagations/propagation_manager.py +3 -5
  170. angr/knowledge_plugins/propagations/propagation_model.py +9 -9
  171. angr/knowledge_plugins/propagations/states.py +52 -22
  172. angr/knowledge_plugins/structured_code/manager.py +2 -2
  173. angr/knowledge_plugins/sync/sync_controller.py +3 -3
  174. angr/knowledge_plugins/variables/variable_access.py +4 -4
  175. angr/knowledge_plugins/variables/variable_manager.py +39 -39
  176. angr/knowledge_plugins/xrefs/xref.py +9 -11
  177. angr/knowledge_plugins/xrefs/xref_manager.py +3 -4
  178. angr/misc/ansi.py +1 -2
  179. angr/misc/autoimport.py +3 -3
  180. angr/misc/plugins.py +9 -9
  181. angr/procedures/definitions/__init__.py +16 -16
  182. angr/procedures/definitions/linux_kernel.py +1 -1
  183. angr/procedures/definitions/parse_win32json.py +1 -1
  184. angr/procedures/java_jni/__init__.py +1 -1
  185. angr/procedures/java_jni/array_operations.py +1 -2
  186. angr/procedures/java_jni/method_calls.py +1 -2
  187. angr/procedures/posix/inet_ntoa.py +1 -2
  188. angr/procedures/stubs/format_parser.py +3 -3
  189. angr/project.py +13 -11
  190. angr/sim_manager.py +12 -12
  191. angr/sim_procedure.py +7 -3
  192. angr/sim_state.py +2 -2
  193. angr/sim_type.py +60 -45
  194. angr/sim_variable.py +5 -5
  195. angr/simos/simos.py +1 -2
  196. angr/simos/userland.py +1 -2
  197. angr/state_plugins/callstack.py +3 -2
  198. angr/state_plugins/history.py +1 -2
  199. angr/state_plugins/solver.py +34 -34
  200. angr/storage/memory_mixins/__init__.py +4 -3
  201. angr/storage/memory_mixins/actions_mixin.py +1 -3
  202. angr/storage/memory_mixins/address_concretization_mixin.py +1 -3
  203. angr/storage/memory_mixins/convenient_mappings_mixin.py +3 -4
  204. angr/storage/memory_mixins/default_filler_mixin.py +1 -1
  205. angr/storage/memory_mixins/label_merger_mixin.py +2 -2
  206. angr/storage/memory_mixins/multi_value_merger_mixin.py +4 -3
  207. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +9 -8
  208. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +12 -11
  209. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +8 -8
  210. angr/storage/memory_mixins/paged_memory/pages/history_tracking_mixin.py +2 -3
  211. angr/storage/memory_mixins/paged_memory/pages/list_page.py +10 -11
  212. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +11 -10
  213. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +18 -17
  214. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +12 -11
  215. angr/storage/memory_mixins/regioned_memory/abstract_address_descriptor.py +3 -3
  216. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +3 -2
  217. angr/storage/memory_mixins/regioned_memory/region_data.py +1 -2
  218. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +2 -2
  219. angr/storage/memory_mixins/regioned_memory/regioned_address_concretization_mixin.py +3 -3
  220. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +18 -21
  221. angr/storage/memory_mixins/size_resolution_mixin.py +1 -2
  222. angr/storage/memory_mixins/symbolic_merger_mixin.py +3 -2
  223. angr/storage/memory_mixins/top_merger_mixin.py +3 -2
  224. angr/storage/memory_object.py +2 -4
  225. angr/utils/algo.py +3 -2
  226. angr/utils/dynamic_dictlist.py +5 -5
  227. angr/utils/formatting.py +4 -4
  228. angr/utils/funcid.py +1 -2
  229. angr/utils/graph.py +5 -6
  230. angr/utils/library.py +5 -5
  231. angr/utils/mp.py +5 -4
  232. angr/utils/segment_list.py +3 -4
  233. angr/utils/typing.py +3 -2
  234. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/METADATA +9 -11
  235. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/RECORD +239 -236
  236. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/LICENSE +0 -0
  237. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/WHEEL +0 -0
  238. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/entry_points.txt +0 -0
  239. {angr-9.2.102.dist-info → angr-9.2.104.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Optional, Tuple, Union, List, DefaultDict, TYPE_CHECKING
1
+ from typing import DefaultDict, TYPE_CHECKING
2
2
  from collections import defaultdict, OrderedDict
3
3
  import logging
4
4
 
@@ -38,12 +38,12 @@ class Case:
38
38
  def __init__(
39
39
  self,
40
40
  original_node,
41
- node_type: Optional[str],
41
+ node_type: str | None,
42
42
  variable_hash,
43
43
  expr,
44
- value: Union[int, str],
44
+ value: int | str,
45
45
  target,
46
- target_idx: Optional[int],
46
+ target_idx: int | None,
47
47
  next_addr,
48
48
  ):
49
49
  self.original_node = original_node
@@ -104,28 +104,28 @@ class StableVarExprHasher(AILBlockWalkerBase):
104
104
  self.walk_expression(expr)
105
105
  self.hash = hash(tuple(self._hash_lst))
106
106
 
107
- def _handle_expr(self, expr_idx: int, expr: Expression, stmt_idx: int, stmt, block: Optional[Block]):
107
+ def _handle_expr(self, expr_idx: int, expr: Expression, stmt_idx: int, stmt, block: Block | None):
108
108
  if hasattr(expr, "variable") and expr.variable is not None:
109
109
  self._hash_lst.append(expr.variable)
110
110
  else:
111
111
  super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
112
112
 
113
- def _handle_Load(self, expr_idx: int, expr: Load, stmt_idx: int, stmt, block: Optional[Block]):
113
+ def _handle_Load(self, expr_idx: int, expr: Load, stmt_idx: int, stmt, block: Block | None):
114
114
  self._hash_lst.append("Load")
115
115
  super()._handle_Load(expr_idx, expr, stmt_idx, stmt, block)
116
116
 
117
- def _handle_BinaryOp(self, expr_idx: int, expr: BinaryOp, stmt_idx: int, stmt, block: Optional[Block]):
117
+ def _handle_BinaryOp(self, expr_idx: int, expr: BinaryOp, stmt_idx: int, stmt, block: Block | None):
118
118
  self._hash_lst.append(expr.op)
119
119
  super()._handle_BinaryOp(expr_idx, expr, stmt_idx, stmt, block)
120
120
 
121
- def _handle_UnaryOp(self, expr_idx: int, expr: "UnaryOp", stmt_idx: int, stmt, block: Optional[Block]):
121
+ def _handle_UnaryOp(self, expr_idx: int, expr: "UnaryOp", stmt_idx: int, stmt, block: Block | None):
122
122
  self._hash_lst.append(expr.op)
123
123
  super()._handle_UnaryOp(expr_idx, expr, stmt_idx, stmt, block)
124
124
 
125
- def _handle_Const(self, expr_idx: int, expr: Const, stmt_idx: int, stmt, block: Optional[Block]):
125
+ def _handle_Const(self, expr_idx: int, expr: Const, stmt_idx: int, stmt, block: Block | None):
126
126
  self._hash_lst.append((expr.value, expr.bits))
127
127
 
128
- def _handle_Convert(self, expr_idx: int, expr: "Convert", stmt_idx: int, stmt, block: Optional[Block]):
128
+ def _handle_Convert(self, expr_idx: int, expr: "Convert", stmt_idx: int, stmt, block: Block | None):
129
129
  self._hash_lst.append(expr.to_bits)
130
130
  super()._handle_Convert(expr_idx, expr, stmt_idx, stmt, block)
131
131
 
@@ -174,7 +174,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
174
174
  original_nodes = original_nodes[1:]
175
175
  existing_nodes_by_addr_and_idx = {(nn.addr, nn.idx): nn for nn in graph_copy}
176
176
 
177
- case_addrs: List[Tuple[Block, Union[int, str], int, Optional[int], int]] = []
177
+ case_addrs: list[tuple[Block, int | str, int, int | None, int]] = []
178
178
  delayed_edges = []
179
179
  for idx, case in enumerate(cases):
180
180
  if idx == 0 or all(
@@ -184,7 +184,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
184
184
  (case.original_node, case.value, case.target, case.target_idx, case.next_addr)
185
185
  )
186
186
  else:
187
- statements: List = [
187
+ statements: list = [
188
188
  stmt for stmt in case.original_node.statements if isinstance(stmt, (Label, Assignment))
189
189
  ]
190
190
  statements.append(
@@ -294,7 +294,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
294
294
  variable_comparisons[node] = ("c",) + r
295
295
  continue
296
296
 
297
- varhash_to_caselists: DefaultDict[int, List[Tuple[List[Case], List]]] = defaultdict(list)
297
+ varhash_to_caselists: DefaultDict[int, list[tuple[list[Case], list]]] = defaultdict(list)
298
298
  used_nodes = set()
299
299
 
300
300
  for head in variable_comparisons:
@@ -516,7 +516,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
516
516
  @staticmethod
517
517
  def _find_switch_variable_comparison_type_a(
518
518
  node,
519
- ) -> Optional[Tuple[int, str, Expression, int, int, Optional[int], int, Optional[int]]]:
519
+ ) -> tuple[int, str, Expression, int, int, int | None, int, int | None] | None:
520
520
  # the type a is the last statement is a var == constant comparison, but
521
521
  # there is more than one non-label statement in the block
522
522
 
@@ -561,7 +561,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
561
561
  @staticmethod
562
562
  def _find_switch_variable_comparison_type_b(
563
563
  node,
564
- ) -> Optional[Tuple[int, str, Expression, int, int, Optional[int], int, Optional[int]]]:
564
+ ) -> tuple[int, str, Expression, int, int, int | None, int, int | None] | None:
565
565
  # the type b is the last statement is a var == constant comparison, and
566
566
  # there is only one non-label statement
567
567
 
@@ -606,7 +606,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
606
606
  @staticmethod
607
607
  def _find_switch_variable_comparison_type_c(
608
608
  node,
609
- ) -> Optional[Tuple[int, str, Expression, int, int, Optional[int], int, Optional[int]]]:
609
+ ) -> tuple[int, str, Expression, int, int, int | None, int, int | None] | None:
610
610
  # the type c is where the last statement is a var < or > constant comparison, and
611
611
  # there is only one non-label statement
612
612
 
@@ -744,7 +744,7 @@ class LoweredSwitchSimplifier(OptimizationPass):
744
744
  remove_last_statement(node)
745
745
 
746
746
  @staticmethod
747
- def cases_issubset(cases_0: List[Case], cases_1: List[Case]) -> bool:
747
+ def cases_issubset(cases_0: list[Case], cases_1: list[Case]) -> bool:
748
748
  """
749
749
  Test if cases_0 is a subset of cases_1.
750
750
  """
@@ -1,5 +1,6 @@
1
1
  # pylint:disable=unused-argument
2
- from typing import Optional, Dict, Set, Tuple, Generator, TYPE_CHECKING
2
+ from typing import TYPE_CHECKING
3
+ from collections.abc import Generator
3
4
  from enum import Enum
4
5
 
5
6
  import networkx # pylint:disable=unused-import
@@ -53,9 +54,7 @@ class BaseOptimizationPass:
53
54
  ARCHES = [] # strings of supported architectures
54
55
  PLATFORMS = [] # strings of supported platforms. Can be one of the following: "win32", "linux"
55
56
  STAGE: int = None # Specifies when this optimization pass should be executed
56
- STRUCTURING: Optional[str] = (
57
- None # specifies if this optimization pass is specific to a certain structuring algorithm
58
- )
57
+ STRUCTURING: str | None = None # specifies if this optimization pass is specific to a certain structuring algorithm
59
58
  NAME = "N/A"
60
59
  DESCRIPTION = "N/A"
61
60
 
@@ -133,23 +132,23 @@ class OptimizationPass(BaseOptimizationPass):
133
132
  ):
134
133
  super().__init__(func)
135
134
  # self._blocks is just a cache
136
- self._blocks_by_addr: Dict[int, Set[ailment.Block]] = blocks_by_addr
137
- self._blocks_by_addr_and_idx: Dict[Tuple[int, Optional[int]], ailment.Block] = blocks_by_addr_and_idx
138
- self._graph: Optional[networkx.DiGraph] = graph
135
+ self._blocks_by_addr: dict[int, set[ailment.Block]] = blocks_by_addr
136
+ self._blocks_by_addr_and_idx: dict[tuple[int, int | None], ailment.Block] = blocks_by_addr_and_idx
137
+ self._graph: networkx.DiGraph | None = graph
139
138
  self._variable_kb = variable_kb
140
139
  self._ri = region_identifier
141
140
  self._rd = reaching_definitions
142
141
  self._new_block_addrs = set()
143
142
 
144
143
  # output
145
- self.out_graph: Optional[networkx.DiGraph] = None
144
+ self.out_graph: networkx.DiGraph | None = None
146
145
 
147
146
  @property
148
- def blocks_by_addr(self) -> Dict[int, Set[ailment.Block]]:
147
+ def blocks_by_addr(self) -> dict[int, set[ailment.Block]]:
149
148
  return self._blocks_by_addr
150
149
 
151
150
  @property
152
- def blocks_by_addr_and_idx(self) -> Dict[Tuple[int, Optional[int]], ailment.Block]:
151
+ def blocks_by_addr_and_idx(self) -> dict[tuple[int, int | None], ailment.Block]:
153
152
  return self._blocks_by_addr_and_idx
154
153
 
155
154
  #
@@ -169,7 +168,7 @@ class OptimizationPass(BaseOptimizationPass):
169
168
  self._new_block_addrs.add(new_addr)
170
169
  return new_addr
171
170
 
172
- def _get_block(self, addr, idx=None) -> Optional[ailment.Block]:
171
+ def _get_block(self, addr, idx=None) -> ailment.Block | None:
173
172
  if not self._blocks_by_addr:
174
173
  return None
175
174
  else:
@@ -288,8 +287,8 @@ class StructuringOptimizationPass(OptimizationPass):
288
287
  self._simplify_ail = simplify_ail
289
288
  self._require_gotos = require_gotos
290
289
 
291
- self._goto_manager: Optional[GotoManager] = None
292
- self._prev_graph: Optional[networkx.DiGraph] = None
290
+ self._goto_manager: GotoManager | None = None
291
+ self._prev_graph: networkx.DiGraph | None = None
293
292
 
294
293
  def _analyze(self, cache=None) -> bool:
295
294
  raise NotImplementedError()
@@ -1,4 +1,4 @@
1
- from typing import List, Tuple, Iterable, Dict
1
+ from collections.abc import Iterable
2
2
  import logging
3
3
 
4
4
  import archinfo
@@ -50,29 +50,33 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
50
50
 
51
51
  return bool(info), {"info": info}
52
52
 
53
+ @staticmethod
54
+ def _modify_statement(
55
+ old_block, stmt_idx_: int, updated_blocks_, stack_offset: int = None
56
+ ): # pylint:disable=unused-argument
57
+ if old_block not in updated_blocks_:
58
+ block = old_block.copy()
59
+ updated_blocks_[old_block] = block
60
+ else:
61
+ block = updated_blocks_[old_block]
62
+ block.statements[stmt_idx_] = None
63
+
53
64
  def _analyze(self, cache=None):
54
- def _remove_statement(old_block, stmt_idx_: int, updated_blocks_):
55
- if old_block not in updated_blocks_:
56
- block = old_block.copy()
57
- updated_blocks[old_block] = block
58
- else:
59
- block = updated_blocks[old_block]
60
- block.statements[stmt_idx_] = None
61
65
 
62
66
  if cache is None:
63
67
  return
64
68
 
65
- info: Dict[int, Dict[str, List[Tuple[int, CodeLocation]]]] = cache["info"]
69
+ info: dict[int, dict[str, list[tuple[int, CodeLocation]]]] = cache["info"]
66
70
  updated_blocks = {}
67
71
 
68
72
  for data in info.values():
69
73
  # remove storing statements
70
- for _, codeloc in data["stored"]:
74
+ for stack_offset, codeloc in data["stored"]:
71
75
  old_block = self._get_block(codeloc.block_addr, idx=codeloc.block_idx)
72
- _remove_statement(old_block, codeloc.stmt_idx, updated_blocks)
73
- for _, codeloc in data["restored"]:
76
+ self._modify_statement(old_block, codeloc.stmt_idx, updated_blocks, stack_offset=stack_offset)
77
+ for stack_offset, codeloc in data["restored"]:
74
78
  old_block = self._get_block(codeloc.block_addr, idx=codeloc.block_idx)
75
- _remove_statement(old_block, codeloc.stmt_idx, updated_blocks)
79
+ self._modify_statement(old_block, codeloc.stmt_idx, updated_blocks, stack_offset=stack_offset)
76
80
 
77
81
  for old_block, new_block in updated_blocks.items():
78
82
  # remove all statements that are None
@@ -80,7 +84,7 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
80
84
  # update it
81
85
  self._update_block(old_block, new_block)
82
86
 
83
- def _find_registers_stored_on_stack(self) -> List[Tuple[int, int, CodeLocation]]:
87
+ def _find_registers_stored_on_stack(self) -> list[tuple[int, int, CodeLocation]]:
84
88
  first_block = self._get_block(self._func.addr)
85
89
  if first_block is None:
86
90
  return []
@@ -101,7 +105,7 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
101
105
 
102
106
  return results
103
107
 
104
- def _find_registers_restored_from_stack(self) -> List[List[Tuple[int, int, CodeLocation]]]:
108
+ def _find_registers_restored_from_stack(self) -> list[list[tuple[int, int, CodeLocation]]]:
105
109
  all_results = []
106
110
  for ret_site in self._func.ret_sites + self._func.jumpout_sites:
107
111
  for block in self._get_blocks(ret_site.addr):
@@ -125,10 +129,10 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
125
129
 
126
130
  def _intersect_register_info(
127
131
  self,
128
- stored: List[Tuple[int, int, CodeLocation]],
129
- restored: Iterable[List[Tuple[int, int, CodeLocation]]],
130
- ) -> Dict[int, Dict[str, List[Tuple[int, CodeLocation]]]]:
131
- def _collect(info: List[Tuple[int, int, CodeLocation]], output, keystr: str):
132
+ stored: list[tuple[int, int, CodeLocation]],
133
+ restored: Iterable[list[tuple[int, int, CodeLocation]]],
134
+ ) -> dict[int, dict[str, list[tuple[int, CodeLocation]]]]:
135
+ def _collect(info: list[tuple[int, int, CodeLocation]], output, keystr: str):
132
136
  for reg_offset, stack_offset, codeloc in info:
133
137
  if reg_offset not in output:
134
138
  output[reg_offset] = {}
@@ -136,7 +140,7 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
136
140
  output[reg_offset][keystr] = []
137
141
  output[reg_offset][keystr].append((stack_offset, codeloc))
138
142
 
139
- result: Dict[int, Dict[str, List[Tuple[int, CodeLocation]]]] = {}
143
+ result: dict[int, dict[str, list[tuple[int, CodeLocation]]]] = {}
140
144
  _collect(stored, result, "stored")
141
145
  for item in restored:
142
146
  _collect(item, result, "restored")
@@ -158,7 +162,7 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
158
162
  lr_reg_offset = self.project.arch.registers["lr"][0]
159
163
  elif self.project.arch.name in {"MIPS32", "MIPS64"}:
160
164
  lr_reg_offset = self.project.arch.registers["ra"][0]
161
- elif self.project.arch.name in {"PPC32", "PPC64"}:
165
+ elif self.project.arch.name in {"PPC32", "PPC64"} or self.project.arch.name.startswith("PowerPC:"):
162
166
  lr_reg_offset = self.project.arch.registers["lr"][0]
163
167
  else:
164
168
  lr_reg_offset = None
@@ -1,4 +1,4 @@
1
- from typing import Tuple, Optional, List, Any
1
+ from typing import Any
2
2
  import logging
3
3
 
4
4
  import ailment
@@ -87,7 +87,7 @@ class RetAddrSaveSimplifier(OptimizationPass):
87
87
  block_copy.statements.pop(stmt_idx)
88
88
  self._update_block(block, block_copy)
89
89
 
90
- def _find_retaddr_save_stmt(self) -> Optional[Tuple[Any, int, ailment.Expr.StackBaseOffset]]:
90
+ def _find_retaddr_save_stmt(self) -> tuple[Any, int, ailment.Expr.StackBaseOffset] | None:
91
91
  """
92
92
  Find the AIL statement that saves the return address to a stack slot.
93
93
 
@@ -126,7 +126,7 @@ class RetAddrSaveSimplifier(OptimizationPass):
126
126
  # Not found
127
127
  return None
128
128
 
129
- def _find_retaddr_restore_stmt(self) -> Optional[List[Tuple[Any, int, ailment.Expr.StackBaseOffset]]]:
129
+ def _find_retaddr_restore_stmt(self) -> list[tuple[Any, int, ailment.Expr.StackBaseOffset]] | None:
130
130
  """
131
131
  Find the AIL statement that restores the return address from a stack slot.
132
132
 
@@ -1,6 +1,5 @@
1
1
  # pylint:disable=unnecessary-pass
2
2
  import logging
3
- from typing import Tuple, List
4
3
 
5
4
  from ailment import Block
6
5
  from ailment.statement import ConditionalJump, Return
@@ -171,7 +170,7 @@ class ReturnDeduplicator(OptimizationPass):
171
170
 
172
171
  return self._get_original_regions(if_ret_candidates)
173
172
 
174
- def _get_original_regions(self, if_ret_candidates: List[Tuple[Block, Block, Block]]):
173
+ def _get_original_regions(self, if_ret_candidates: list[tuple[Block, Block, Block]]):
175
174
  """
176
175
  Input: [(if_stmt_block, super_true_child, super_false_child), ...]
177
176
  Output: [(if_stmt_block, true_child, false_child, super_true_child, super_false_child), ...]
@@ -1,4 +1,4 @@
1
- from typing import Any, Tuple, Dict, List, Optional
1
+ from typing import Any
2
2
  from itertools import count
3
3
  import copy
4
4
  import logging
@@ -32,7 +32,7 @@ class ReturnDuplicatorBase:
32
32
  node_idx_start: int = 0,
33
33
  max_calls_in_regions: int = 2,
34
34
  minimize_copies_for_regions: bool = True,
35
- ri: Optional[RegionIdentifier] = None,
35
+ ri: RegionIdentifier | None = None,
36
36
  **kwargs,
37
37
  ):
38
38
  self.node_idx = count(start=node_idx_start)
@@ -41,7 +41,7 @@ class ReturnDuplicatorBase:
41
41
 
42
42
  # this should also be set by the optimization passes initer
43
43
  self._func = func
44
- self._ri: Optional[RegionIdentifier] = ri
44
+ self._ri: RegionIdentifier | None = ri
45
45
 
46
46
  #
47
47
  # must implement these methods
@@ -88,7 +88,7 @@ class ReturnDuplicatorBase:
88
88
 
89
89
  return graph_changed
90
90
 
91
- def _find_endnode_regions(self, graph) -> Dict[Any, Tuple[List[Tuple[Any, Any]], networkx.DiGraph]]:
91
+ def _find_endnode_regions(self, graph) -> dict[Any, tuple[list[tuple[Any, Any]], networkx.DiGraph]]:
92
92
  """
93
93
  Find all the regions that contain a node with no successors. These are the "end nodes" of the graph.
94
94
  """
@@ -97,7 +97,7 @@ class ReturnDuplicatorBase:
97
97
  # to_update is keyed by the region head.
98
98
  # this is because different end nodes may lead to the same region head: consider the case of the typical "fork"
99
99
  # region where stack canary is checked in x86-64 binaries.
100
- end_node_regions: Dict[Any, Tuple[List[Tuple[Any, Any]], networkx.DiGraph]] = {}
100
+ end_node_regions: dict[Any, tuple[list[tuple[Any, Any]], networkx.DiGraph]] = {}
101
101
 
102
102
  for end_node in endnodes:
103
103
  in_edges = list(graph.in_edges(end_node))
@@ -175,7 +175,7 @@ class ReturnDuplicatorBase:
175
175
  graph.remove_edge(pred_node, region_head)
176
176
 
177
177
  def _copy_connected_edge_components(
178
- self, endnode_regions: Dict[Any, Tuple[List[Tuple[Any, Any]], networkx.DiGraph]], graph: networkx.DiGraph
178
+ self, endnode_regions: dict[Any, tuple[list[tuple[Any, Any]], networkx.DiGraph]], graph: networkx.DiGraph
179
179
  ):
180
180
  updated_regions = endnode_regions.copy()
181
181
  all_region_block_addrs = list(self._find_block_sets_in_all_regions(self._ri.region).values())
@@ -312,7 +312,7 @@ class ReturnDuplicatorBase:
312
312
  return valid_assignment
313
313
 
314
314
  @staticmethod
315
- def _single_entry_region(graph, end_node) -> Tuple[networkx.DiGraph, Any]:
315
+ def _single_entry_region(graph, end_node) -> tuple[networkx.DiGraph, Any]:
316
316
  """
317
317
  Back track on the graph from `end_node` and find the longest chain of nodes where each node has only one
318
318
  predecessor and one successor (the second-to-last node may have two successors to account for the typical
@@ -0,0 +1,18 @@
1
+ import logging
2
+
3
+ from .register_save_area_simplifier import RegisterSaveAreaSimplifier
4
+
5
+
6
+ _l = logging.getLogger(name=__name__)
7
+
8
+
9
+ class SpilledRegisterFinder(RegisterSaveAreaSimplifier):
10
+ """
11
+ Finds spilled registers and tags them with pseudoregisters based on their stack offset.
12
+ """
13
+
14
+ @staticmethod
15
+ def _modify_statement(old_block, stmt_idx_: int, updated_blocks_, stack_offset: int = None):
16
+ old_stmt = old_block.statements[stmt_idx_]
17
+ pseudoreg = 0x1000000 - stack_offset
18
+ old_stmt.tags["pseudoreg"] = pseudoreg
@@ -1,4 +1,3 @@
1
- from typing import Set, Dict
2
1
  from collections import defaultdict
3
2
  import logging
4
3
 
@@ -74,7 +73,7 @@ class StackCanarySimplifier(OptimizationPass):
74
73
  # Before node duplication, each pair of canary-check-success and canary-check-failure nodes have a common
75
74
  # predecessor.
76
75
  # map endpoint addrs to their common predecessors
77
- pred_addr_to_endpoint_addrs: Dict[int, Set[int]] = defaultdict(set)
76
+ pred_addr_to_endpoint_addrs: dict[int, set[int]] = defaultdict(set)
78
77
  for node_addr in all_endpoint_addrs:
79
78
  preds = self._func.graph.predecessors(self._func.get_node(node_addr))
80
79
  for pred in preds:
@@ -183,7 +182,7 @@ class StackCanarySimplifier(OptimizationPass):
183
182
 
184
183
  while True:
185
184
  traversed.add(block_addr)
186
- first_block = self._get_block(block_addr)
185
+ first_block = next(self._get_blocks(block_addr))
187
186
  if first_block is None:
188
187
  break
189
188
 
@@ -1,5 +1,4 @@
1
1
  # pylint:disable=too-many-boolean-expressions
2
- from typing import Set, Dict
3
2
  from collections import defaultdict
4
3
  import logging
5
4
 
@@ -90,7 +89,7 @@ class WinStackCanarySimplifier(OptimizationPass):
90
89
  # Before node duplication, each pair of canary-check-success and canary-check-failure nodes have a common
91
90
  # predecessor.
92
91
  # map endpoint addrs to their common predecessors
93
- pred_addr_to_endpoint_addrs: Dict[int, Set[int]] = defaultdict(set)
92
+ pred_addr_to_endpoint_addrs: dict[int, set[int]] = defaultdict(set)
94
93
  for node_addr in all_endpoint_addrs:
95
94
  preds = self._func.graph.predecessors(self._func.get_node(node_addr))
96
95
  for pred in preds:
@@ -1,4 +1,4 @@
1
- from typing import Tuple, List, Any
1
+ from typing import Any
2
2
  import logging
3
3
 
4
4
  import ailment
@@ -60,7 +60,7 @@ class X86GccGetPcSimplifier(OptimizationPass):
60
60
  block.statements = block.statements[: stmt_idx - 1] + block.statements[stmt_idx:]
61
61
  self._update_block(old_block, block)
62
62
 
63
- def _find_getpc_calls(self) -> List[Tuple[Any, int, str, int]]:
63
+ def _find_getpc_calls(self) -> list[tuple[Any, int, str, int]]:
64
64
  """
65
65
  Find all blocks that are calling __x86.get_pc_thunk functions.
66
66
 
@@ -43,12 +43,13 @@ from .rol_ror import RolRorRewriter
43
43
  from .inlined_strcpy import InlinedStrcpy
44
44
  from .inlined_strcpy_consolidation import InlinedStrcpyConsolidation
45
45
  from .inlined_wstrcpy import InlinedWstrcpy
46
+ from .cmpord_rewriter import CmpORDRewriter
46
47
 
47
48
  from .base import PeepholeOptimizationExprBase, PeepholeOptimizationStmtBase, PeepholeOptimizationMultiStmtBase
48
49
 
49
- MULTI_STMT_OPTS: List[Type[PeepholeOptimizationMultiStmtBase]] = []
50
- STMT_OPTS: List[Type[PeepholeOptimizationStmtBase]] = []
51
- EXPR_OPTS: List[Type[PeepholeOptimizationExprBase]] = []
50
+ MULTI_STMT_OPTS: list[type[PeepholeOptimizationMultiStmtBase]] = []
51
+ STMT_OPTS: list[type[PeepholeOptimizationStmtBase]] = []
52
+ EXPR_OPTS: list[type[PeepholeOptimizationExprBase]] = []
52
53
 
53
54
  _g = globals().copy()
54
55
  for v in _g.values():
@@ -1,5 +1,3 @@
1
- from typing import List, Optional
2
-
3
1
  from ailment.expression import BinaryOp, UnaryOp, Expression
4
2
  from ailment.statement import Statement, Assignment
5
3
  from ailment import Block
@@ -17,15 +15,15 @@ class PeepholeOptimizationStmtBase:
17
15
  "kb",
18
16
  "func_addr",
19
17
  )
20
- project: Optional[Project]
21
- kb: Optional[KnowledgeBase]
22
- func_addr: Optional[int]
18
+ project: Project | None
19
+ kb: KnowledgeBase | None
20
+ func_addr: int | None
23
21
 
24
22
  NAME = "Peephole Optimization - Statement"
25
23
  DESCRIPTION = "Peephole Optimization - Statement"
26
24
  stmt_classes = None
27
25
 
28
- def __init__(self, project: Optional[Project], kb: Optional[KnowledgeBase], func_addr: Optional[int] = None):
26
+ def __init__(self, project: Project | None, kb: KnowledgeBase | None, func_addr: int | None = None):
29
27
  self.project = project
30
28
  self.kb = kb
31
29
  self.func_addr = func_addr
@@ -44,20 +42,20 @@ class PeepholeOptimizationMultiStmtBase:
44
42
  "kb",
45
43
  "func_addr",
46
44
  )
47
- project: Optional[Project]
48
- kb: Optional[KnowledgeBase]
49
- func_addr: Optional[int]
45
+ project: Project | None
46
+ kb: KnowledgeBase | None
47
+ func_addr: int | None
50
48
 
51
49
  NAME = "Peephole Optimization - Multi-statement"
52
50
  DESCRIPTION = "Peephole Optimization - Multi-statement"
53
51
  stmt_classes = None
54
52
 
55
- def __init__(self, project: Optional[Project], kb: Optional[KnowledgeBase], func_addr: Optional[int] = None):
53
+ def __init__(self, project: Project | None, kb: KnowledgeBase | None, func_addr: int | None = None):
56
54
  self.project = project
57
55
  self.kb = kb
58
56
  self.func_addr = func_addr
59
57
 
60
- def optimize(self, stmts: List[Statement], stmt_idx: Optional[int] = None, block=None, **kwargs):
58
+ def optimize(self, stmts: list[Statement], stmt_idx: int | None = None, block=None, **kwargs):
61
59
  raise NotImplementedError("_optimize() is not implemented.")
62
60
 
63
61
 
@@ -71,15 +69,15 @@ class PeepholeOptimizationExprBase:
71
69
  "kb",
72
70
  "func_addr",
73
71
  )
74
- project: Optional[Project]
75
- kb: Optional[KnowledgeBase]
76
- func_addr: Optional[int]
72
+ project: Project | None
73
+ kb: KnowledgeBase | None
74
+ func_addr: int | None
77
75
 
78
76
  NAME = "Peephole Optimization - Expression"
79
77
  DESCRIPTION = "Peephole Optimization - Expression"
80
78
  expr_classes = None
81
79
 
82
- def __init__(self, project: Optional[Project], kb: Optional[KnowledgeBase], func_addr: Optional[int] = None):
80
+ def __init__(self, project: Project | None, kb: KnowledgeBase | None, func_addr: int | None = None):
83
81
  self.project = project
84
82
  self.kb = kb
85
83
  self.func_addr = func_addr
@@ -1,5 +1,3 @@
1
- from typing import Tuple, Optional
2
-
3
1
  from ailment.expression import BinaryOp, Const, Expression, Convert
4
2
  from ailment.statement import Call
5
3
 
@@ -101,7 +99,7 @@ class Bswap(PeepholeOptimizationExprBase):
101
99
 
102
100
  return None
103
101
 
104
- def _match_inner(self, or_first: BinaryOp, or_second: BinaryOp) -> Tuple[bool, Optional[Expression]]:
102
+ def _match_inner(self, or_first: BinaryOp, or_second: BinaryOp) -> tuple[bool, Expression | None]:
105
103
  if isinstance(or_first.operands[1], Const) and or_first.operands[1].value == 0xFF00FF00:
106
104
  if isinstance(or_second.operands[1], Const) and or_second.operands[1].value == 0x00FF00FF:
107
105
  inner_first = or_first.operands[0]
@@ -0,0 +1,72 @@
1
+ from ailment.statement import ConditionalJump
2
+ from ailment.expression import BinaryOp, Const
3
+
4
+ from .base import PeepholeOptimizationStmtBase
5
+
6
+
7
+ class CmpORDRewriter(PeepholeOptimizationStmtBase):
8
+ """
9
+ Rewrites CmpORD expressions (PowerPC and VEX-specific) into common comparison operations.
10
+ """
11
+
12
+ __slots__ = ()
13
+
14
+ NAME = "CmpORD rewriter"
15
+ stmt_classes = (ConditionalJump,)
16
+
17
+ def optimize(self, stmt: ConditionalJump, stmt_idx: int = None, block=None, **kwargs):
18
+ # example:
19
+ # 05 | 0x4011d4 | if ((((gpr9<4> CmpORD 0x0<32>) & 0x2<32>) != 0x0<32>)) { Goto ... } else { Goto ... }
20
+ # or
21
+ # 02 | 0x401260 | if (((((gpr3<4> CmpORD 0x0<32>) & 0x2<32>) ^ 0x2<32>) != 0x0<32>)) { Goto ... } else
22
+ # { Goto ... }
23
+
24
+ if not isinstance(stmt.condition, BinaryOp) or stmt.condition.op not in {"CmpNE", "CmpEQ"}:
25
+ return None
26
+ cmp_rhs = stmt.condition.operands[1]
27
+ if not isinstance(cmp_rhs, Const) or cmp_rhs.value != 0:
28
+ return None
29
+ negated = stmt.condition.op == "CmpEQ"
30
+
31
+ cmp_lhs = stmt.condition.operands[0]
32
+ xor_value = None
33
+ if isinstance(cmp_lhs, BinaryOp) and cmp_lhs.op == "Xor" and isinstance(cmp_lhs.operands[1], Const):
34
+ negated = not negated
35
+ xor_value = cmp_lhs.operands[1].value
36
+ # unpack
37
+ cmp_lhs = cmp_lhs.operands[0]
38
+
39
+ if not isinstance(cmp_lhs, BinaryOp) or cmp_lhs.op != "And":
40
+ return None
41
+
42
+ if not isinstance(cmp_lhs.operands[1], Const) or cmp_lhs.operands[1].value not in {2, 4, 8}:
43
+ return None
44
+ if xor_value is not None and cmp_lhs.operands[1].value != xor_value:
45
+ return None
46
+
47
+ real_cmp = cmp_lhs.operands[0]
48
+ if not isinstance(real_cmp, BinaryOp) or real_cmp.op != "CmpORD":
49
+ return None
50
+
51
+ # determine the real comparison operator
52
+ match cmp_lhs.operands[1].value:
53
+ case 2:
54
+ cmp_op = "CmpEQ" if not negated else "CmpNE"
55
+ case 4:
56
+ cmp_op = "CmpGE" if not negated else "CmpLT"
57
+ case _: # case 8
58
+ cmp_op = "CmpLT" if not negated else "CmpGE"
59
+
60
+ # generate the new comparison
61
+ new_cond = BinaryOp(stmt.condition.idx, cmp_op, real_cmp.operands[::], real_cmp.signed, **real_cmp.tags)
62
+ new_stmt = ConditionalJump(
63
+ stmt.idx,
64
+ new_cond,
65
+ stmt.true_target,
66
+ stmt.false_target,
67
+ stmt.true_target_idx,
68
+ stmt.false_target_idx,
69
+ **stmt.tags,
70
+ )
71
+
72
+ return new_stmt
@@ -1,5 +1,4 @@
1
1
  # pylint:disable=too-many-boolean-expressions
2
- from typing import Union
3
2
 
4
3
  from ailment.expression import Convert, BinaryOp, Const
5
4
 
@@ -16,7 +15,7 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
16
15
  NAME = "Conv(64->32, (N * a) >> M) => a / N1"
17
16
  expr_classes = (Convert, BinaryOp)
18
17
 
19
- def optimize(self, expr: Union[Convert, BinaryOp], **kwargs):
18
+ def optimize(self, expr: Convert | BinaryOp, **kwargs):
20
19
  r = None
21
20
 
22
21
  if isinstance(expr, Convert):