angr 9.2.158__cp310-abi3-win_amd64.whl → 9.2.159__cp310-abi3-win_amd64.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 (192) hide show
  1. angr/__init__.py +1 -1
  2. angr/ailment/__init__.py +81 -0
  3. angr/ailment/block.py +81 -0
  4. angr/ailment/block_walker.py +845 -0
  5. angr/ailment/constant.py +3 -0
  6. angr/ailment/converter_common.py +11 -0
  7. angr/ailment/converter_pcode.py +623 -0
  8. angr/ailment/converter_vex.py +798 -0
  9. angr/ailment/expression.py +1639 -0
  10. angr/ailment/manager.py +33 -0
  11. angr/ailment/statement.py +978 -0
  12. angr/ailment/tagged_object.py +61 -0
  13. angr/ailment/utils.py +114 -0
  14. angr/analyses/calling_convention/calling_convention.py +6 -2
  15. angr/analyses/decompiler/ail_simplifier.py +5 -5
  16. angr/analyses/decompiler/block_io_finder.py +4 -4
  17. angr/analyses/decompiler/block_similarity.py +2 -2
  18. angr/analyses/decompiler/block_simplifier.py +4 -4
  19. angr/analyses/decompiler/callsite_maker.py +2 -2
  20. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
  21. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +1 -1
  22. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +1 -1
  23. angr/analyses/decompiler/clinic.py +1 -1
  24. angr/analyses/decompiler/condition_processor.py +1 -1
  25. angr/analyses/decompiler/counters/boolean_counter.py +4 -4
  26. angr/analyses/decompiler/counters/call_counter.py +4 -4
  27. angr/analyses/decompiler/counters/expression_counters.py +5 -5
  28. angr/analyses/decompiler/counters/seq_cf_structure_counter.py +1 -1
  29. angr/analyses/decompiler/decompiler.py +5 -3
  30. angr/analyses/decompiler/dephication/dephication_base.py +12 -1
  31. angr/analyses/decompiler/dephication/graph_dephication.py +12 -5
  32. angr/analyses/decompiler/dephication/graph_rewriting.py +6 -10
  33. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +109 -72
  34. angr/analyses/decompiler/dephication/rewriting_engine.py +32 -9
  35. angr/analyses/decompiler/dephication/seqnode_dephication.py +32 -10
  36. angr/analyses/decompiler/empty_node_remover.py +2 -2
  37. angr/analyses/decompiler/expression_narrower.py +6 -6
  38. angr/analyses/decompiler/goto_manager.py +2 -2
  39. angr/analyses/decompiler/jump_target_collector.py +1 -1
  40. angr/analyses/decompiler/label_collector.py +1 -1
  41. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +25 -25
  42. angr/analyses/decompiler/optimization_passes/call_stmt_rewriter.py +1 -1
  43. angr/analyses/decompiler/optimization_passes/code_motion.py +2 -2
  44. angr/analyses/decompiler/optimization_passes/condition_constprop.py +3 -3
  45. angr/analyses/decompiler/optimization_passes/const_derefs.py +3 -3
  46. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +4 -4
  47. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +2 -2
  48. angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +3 -3
  49. angr/analyses/decompiler/optimization_passes/div_simplifier.py +1 -1
  50. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
  51. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +4 -4
  52. angr/analyses/decompiler/optimization_passes/duplication_reverter/similarity.py +1 -1
  53. angr/analyses/decompiler/optimization_passes/duplication_reverter/utils.py +4 -4
  54. angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +3 -3
  55. angr/analyses/decompiler/optimization_passes/engine_base.py +1 -1
  56. angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +3 -3
  57. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
  58. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +2 -2
  59. angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +3 -3
  60. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
  61. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +4 -4
  62. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +1 -1
  63. angr/analyses/decompiler/optimization_passes/optimization_pass.py +25 -1
  64. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +1 -1
  65. angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +1 -1
  66. angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +2 -2
  67. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +4 -4
  68. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -2
  69. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
  70. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +3 -3
  71. angr/analyses/decompiler/optimization_passes/switch_reused_entry_rewriter.py +3 -3
  72. angr/analyses/decompiler/optimization_passes/tag_slicer.py +1 -1
  73. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +1 -1
  74. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +1 -1
  75. angr/analyses/decompiler/peephole_optimizations/a_div_const_add_a_mul_n_div_const.py +1 -1
  76. angr/analyses/decompiler/peephole_optimizations/a_mul_const_div_shr_const.py +1 -1
  77. angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +1 -1
  78. angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +1 -1
  79. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div.py +1 -1
  80. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +1 -1
  81. angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +1 -1
  82. angr/analyses/decompiler/peephole_optimizations/a_sub_a_sub_n.py +1 -1
  83. angr/analyses/decompiler/peephole_optimizations/arm_cmpf.py +1 -1
  84. angr/analyses/decompiler/peephole_optimizations/base.py +3 -3
  85. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_add_n.py +1 -1
  86. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_and_mask.py +1 -1
  87. angr/analyses/decompiler/peephole_optimizations/bitwise_or_to_logical_or.py +1 -1
  88. angr/analyses/decompiler/peephole_optimizations/bool_expr_xor_1.py +1 -1
  89. angr/analyses/decompiler/peephole_optimizations/bswap.py +2 -2
  90. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +2 -2
  91. angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +2 -2
  92. angr/analyses/decompiler/peephole_optimizations/coalesce_adjacent_shrs.py +1 -1
  93. angr/analyses/decompiler/peephole_optimizations/coalesce_same_cascading_ifs.py +2 -2
  94. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +1 -1
  95. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
  96. angr/analyses/decompiler/peephole_optimizations/conv_a_sub0_shr_and.py +1 -1
  97. angr/analyses/decompiler/peephole_optimizations/conv_shl_shr.py +1 -1
  98. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +1 -1
  99. angr/analyses/decompiler/peephole_optimizations/extended_byte_and_mask.py +1 -1
  100. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +2 -2
  101. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +2 -2
  102. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +2 -2
  103. angr/analyses/decompiler/peephole_optimizations/invert_negated_logical_conjuction_disjunction.py +1 -1
  104. angr/analyses/decompiler/peephole_optimizations/one_sub_bool.py +1 -1
  105. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +1 -1
  106. angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +2 -2
  107. angr/analyses/decompiler/peephole_optimizations/remove_empty_if_body.py +2 -2
  108. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +1 -1
  109. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +1 -1
  110. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +1 -1
  111. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_branch.py +1 -1
  112. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_comparisons.py +1 -1
  113. angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +1 -1
  114. angr/analyses/decompiler/peephole_optimizations/remove_redundant_reinterprets.py +1 -1
  115. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +1 -1
  116. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +1 -1
  117. angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +1 -1
  118. angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +1 -1
  119. angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +3 -3
  120. angr/analyses/decompiler/peephole_optimizations/rewrite_mips_gp_loads.py +1 -1
  121. angr/analyses/decompiler/peephole_optimizations/rol_ror.py +2 -2
  122. angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +1 -1
  123. angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +1 -1
  124. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +1 -1
  125. angr/analyses/decompiler/peephole_optimizations/single_bit_cond_to_boolexpr.py +1 -1
  126. angr/analyses/decompiler/peephole_optimizations/single_bit_xor.py +1 -1
  127. angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +2 -2
  128. angr/analyses/decompiler/peephole_optimizations/utils.py +1 -1
  129. angr/analyses/decompiler/redundant_label_remover.py +1 -1
  130. angr/analyses/decompiler/region_identifier.py +4 -4
  131. angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +1 -1
  132. angr/analyses/decompiler/region_simplifiers/cascading_ifs.py +1 -1
  133. angr/analyses/decompiler/region_simplifiers/expr_folding.py +37 -8
  134. angr/analyses/decompiler/region_simplifiers/goto.py +1 -1
  135. angr/analyses/decompiler/region_simplifiers/if_.py +1 -1
  136. angr/analyses/decompiler/region_simplifiers/loop.py +1 -1
  137. angr/analyses/decompiler/region_simplifiers/node_address_finder.py +1 -1
  138. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +14 -2
  139. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +3 -3
  140. angr/analyses/decompiler/region_simplifiers/switch_expr_simplifier.py +1 -1
  141. angr/analyses/decompiler/return_maker.py +1 -1
  142. angr/analyses/decompiler/seq_to_blocks.py +1 -1
  143. angr/analyses/decompiler/sequence_walker.py +2 -2
  144. angr/analyses/decompiler/ssailification/rewriting.py +4 -4
  145. angr/analyses/decompiler/ssailification/rewriting_engine.py +4 -4
  146. angr/analyses/decompiler/ssailification/rewriting_state.py +3 -3
  147. angr/analyses/decompiler/ssailification/ssailification.py +2 -2
  148. angr/analyses/decompiler/ssailification/traversal.py +1 -1
  149. angr/analyses/decompiler/ssailification/traversal_engine.py +11 -2
  150. angr/analyses/decompiler/structured_codegen/c.py +3 -3
  151. angr/analyses/decompiler/structuring/dream.py +1 -1
  152. angr/analyses/decompiler/structuring/phoenix.py +3 -3
  153. angr/analyses/decompiler/structuring/structurer_base.py +1 -1
  154. angr/analyses/decompiler/structuring/structurer_nodes.py +1 -2
  155. angr/analyses/decompiler/utils.py +1 -1
  156. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
  157. angr/analyses/deobfuscator/string_obf_opt_passes.py +3 -3
  158. angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +2 -2
  159. angr/analyses/propagator/propagator.py +1 -1
  160. angr/analyses/proximity_graph.py +2 -2
  161. angr/analyses/reaching_definitions/engine_ail.py +1 -1
  162. angr/analyses/reaching_definitions/reaching_definitions.py +1 -1
  163. angr/analyses/reaching_definitions/subject.py +1 -1
  164. angr/analyses/s_liveness.py +2 -2
  165. angr/analyses/s_propagator.py +3 -3
  166. angr/analyses/s_reaching_definitions/s_rda_model.py +1 -1
  167. angr/analyses/s_reaching_definitions/s_rda_view.py +3 -3
  168. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -3
  169. angr/analyses/variable_recovery/engine_ail.py +2 -2
  170. angr/analyses/variable_recovery/engine_base.py +1 -1
  171. angr/analyses/variable_recovery/variable_recovery_base.py +1 -1
  172. angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
  173. angr/engines/light/data.py +1 -1
  174. angr/engines/light/engine.py +1 -1
  175. angr/knowledge_plugins/key_definitions/atoms.py +1 -1
  176. angr/knowledge_plugins/propagations/prop_value.py +1 -1
  177. angr/knowledge_plugins/propagations/propagation_model.py +1 -1
  178. angr/knowledge_plugins/propagations/states.py +1 -1
  179. angr/knowledge_plugins/variables/variable_manager.py +1 -1
  180. angr/lib/angr_native.dll +0 -0
  181. angr/rustylib.pyd +0 -0
  182. angr/utils/ail.py +4 -4
  183. angr/utils/endness.py +1 -1
  184. angr/utils/ssa/__init__.py +14 -4
  185. angr/utils/ssa/tmp_uses_collector.py +4 -4
  186. angr/utils/ssa/vvar_uses_collector.py +4 -4
  187. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/METADATA +6 -6
  188. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/RECORD +192 -180
  189. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/WHEEL +0 -0
  190. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/entry_points.txt +0 -0
  191. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/licenses/LICENSE +0 -0
  192. {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1639 @@
1
+ # pylint:disable=arguments-renamed,isinstance-second-argument-not-valid-type,missing-class-docstring,too-many-boolean-expressions
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING, cast
4
+ from collections.abc import Sequence
5
+ from enum import Enum, IntEnum
6
+ from abc import abstractmethod
7
+ from typing_extensions import Self
8
+
9
+
10
+ try:
11
+ import claripy
12
+ except ImportError:
13
+ claripy = None
14
+
15
+ from .tagged_object import TaggedObject
16
+ from .utils import get_bits, stable_hash, is_none_or_likeable, is_none_or_matchable
17
+
18
+ if TYPE_CHECKING:
19
+ from .statement import Statement
20
+
21
+
22
+ class Expression(TaggedObject):
23
+ """
24
+ The base class of all AIL expressions.
25
+ """
26
+
27
+ bits: int
28
+
29
+ __slots__ = (
30
+ "bits",
31
+ "depth",
32
+ )
33
+
34
+ def __init__(self, idx, depth, **kwargs):
35
+ super().__init__(idx, **kwargs)
36
+ self.depth = depth
37
+
38
+ @abstractmethod
39
+ def __repr__(self):
40
+ raise NotImplementedError
41
+
42
+ def has_atom(self, atom, identity=True):
43
+ if identity:
44
+ return self is atom
45
+ return self.likes(atom)
46
+
47
+ def __eq__(self, other):
48
+ if self is other:
49
+ return True
50
+ return type(self) is type(other) and self.likes(other) and self.idx == other.idx
51
+
52
+ @abstractmethod
53
+ def likes(self, other): # pylint:disable=unused-argument,no-self-use
54
+ raise NotImplementedError
55
+
56
+ @abstractmethod
57
+ def matches(self, other): # pylint:disable=unused-argument,no-self-use
58
+ raise NotImplementedError
59
+
60
+ def replace(self, old_expr: Expression, new_expr: Expression) -> tuple[bool, Self]:
61
+ if self is old_expr:
62
+ r = True
63
+ replaced = cast(Self, new_expr)
64
+ elif not isinstance(self, Atom):
65
+ r, replaced = self.replace(old_expr, new_expr)
66
+ else:
67
+ r, replaced = False, self
68
+
69
+ return r, replaced
70
+
71
+ def __add__(self, other):
72
+ return BinaryOp(None, "Add", [self, other], signed=False, **self.tags)
73
+
74
+ def __sub__(self, other):
75
+ return BinaryOp(None, "Sub", [self, other], signed=False, **self.tags)
76
+
77
+
78
+ class Atom(Expression):
79
+ __slots__ = (
80
+ "variable",
81
+ "variable_offset",
82
+ )
83
+
84
+ def __init__(self, idx: int | None, variable=None, variable_offset=0, **kwargs):
85
+ super().__init__(idx, 0, **kwargs)
86
+ self.variable = variable
87
+ self.variable_offset = variable_offset
88
+
89
+ def __repr__(self) -> str:
90
+ return f"Atom ({self.idx})"
91
+
92
+ def copy(self) -> Self: # pylint:disable=no-self-use
93
+ raise NotImplementedError
94
+
95
+
96
+ class Const(Atom):
97
+ __slots__ = ("value",)
98
+
99
+ def __init__(self, idx: int | None, variable, value: int | float, bits: int, **kwargs):
100
+ super().__init__(idx, variable, **kwargs)
101
+
102
+ self.value = value
103
+ self.bits = bits
104
+
105
+ @property
106
+ def size(self):
107
+ return self.bits // 8
108
+
109
+ def __repr__(self):
110
+ return str(self)
111
+
112
+ def __str__(self):
113
+ if isinstance(self.value, int):
114
+ return f"{self.value:#x}<{self.bits}>"
115
+ if isinstance(self.value, float):
116
+ return f"{self.value:f}<{self.bits}>"
117
+ return f"{self.value}<{self.bits}>"
118
+
119
+ def likes(self, other):
120
+ # nan is nan, but nan != nan
121
+ return (
122
+ type(self) is type(other)
123
+ and (self.value is other.value or self.value == other.value)
124
+ and self.bits == other.bits
125
+ )
126
+
127
+ matches = likes
128
+ __hash__ = TaggedObject.__hash__ # type: ignore
129
+
130
+ def _hash_core(self):
131
+ return stable_hash((self.value, self.bits))
132
+
133
+ @property
134
+ def sign_bit(self):
135
+ if not self.is_int:
136
+ raise TypeError("Sign bit is only available for int constants.")
137
+ assert isinstance(self.value, int)
138
+ return self.value >> (self.bits - 1)
139
+
140
+ def copy(self) -> Const:
141
+ return Const(self.idx, self.variable, self.value, self.bits, **self.tags)
142
+
143
+ @property
144
+ def is_int(self) -> bool:
145
+ return isinstance(self.value, int)
146
+
147
+
148
+ class Tmp(Atom):
149
+ __slots__ = ("tmp_idx",)
150
+
151
+ def __init__(self, idx: int | None, variable, tmp_idx: int, bits, **kwargs):
152
+ super().__init__(idx, variable, **kwargs)
153
+
154
+ self.tmp_idx = tmp_idx
155
+ self.bits = bits
156
+
157
+ @property
158
+ def size(self):
159
+ return self.bits // 8
160
+
161
+ def __repr__(self):
162
+ return str(self)
163
+
164
+ def __str__(self):
165
+ return f"t{self.tmp_idx}"
166
+
167
+ def likes(self, other):
168
+ return type(self) is type(other) and self.tmp_idx == other.tmp_idx and self.bits == other.bits
169
+
170
+ matches = likes
171
+ __hash__ = TaggedObject.__hash__ # type: ignore
172
+
173
+ def _hash_core(self):
174
+ return stable_hash(("tmp", self.tmp_idx, self.bits))
175
+
176
+ def copy(self) -> Tmp:
177
+ return Tmp(self.idx, self.variable, self.tmp_idx, self.bits, **self.tags)
178
+
179
+
180
+ class Register(Atom):
181
+ __slots__ = ("reg_offset",)
182
+
183
+ def __init__(self, idx: int | None, variable, reg_offset: int, bits: int, **kwargs):
184
+ super().__init__(idx, variable, **kwargs)
185
+
186
+ self.reg_offset = reg_offset
187
+ self.bits = bits
188
+
189
+ @property
190
+ def size(self):
191
+ return self.bits // 8
192
+
193
+ def likes(self, other):
194
+ return type(self) is type(other) and self.reg_offset == other.reg_offset and self.bits == other.bits
195
+
196
+ def __repr__(self):
197
+ return str(self)
198
+
199
+ def __str__(self):
200
+ if hasattr(self, "reg_name"):
201
+ return f"{self.reg_name}<{self.bits // 8}>"
202
+ if self.variable is None:
203
+ return f"reg_{self.reg_offset}<{self.bits // 8}>"
204
+ return f"{self.variable.name!s}"
205
+
206
+ matches = likes
207
+ __hash__ = TaggedObject.__hash__ # type: ignore
208
+
209
+ def _hash_core(self):
210
+ return stable_hash(("reg", self.reg_offset, self.bits, self.idx))
211
+
212
+ def copy(self) -> Register:
213
+ return Register(self.idx, self.variable, self.reg_offset, self.bits, **self.tags)
214
+
215
+
216
+ class VirtualVariableCategory(IntEnum):
217
+ REGISTER = 0
218
+ STACK = 1
219
+ MEMORY = 2
220
+ PARAMETER = 3
221
+ TMP = 4
222
+ UNKNOWN = 5
223
+
224
+
225
+ class VirtualVariable(Atom):
226
+
227
+ __slots__ = (
228
+ "category",
229
+ "oident",
230
+ "varid",
231
+ )
232
+
233
+ def __init__(
234
+ self,
235
+ idx,
236
+ varid: int,
237
+ bits,
238
+ category: VirtualVariableCategory,
239
+ oident: int | str | tuple | None = None,
240
+ **kwargs,
241
+ ):
242
+ super().__init__(idx, **kwargs)
243
+
244
+ self.varid = varid
245
+ self.category = category
246
+ self.oident = oident
247
+ self.bits = bits
248
+
249
+ @property
250
+ def size(self):
251
+ return self.bits // 8
252
+
253
+ @property
254
+ def was_reg(self) -> bool:
255
+ return self.category == VirtualVariableCategory.REGISTER
256
+
257
+ @property
258
+ def was_stack(self) -> bool:
259
+ return self.category == VirtualVariableCategory.STACK
260
+
261
+ @property
262
+ def was_parameter(self) -> bool:
263
+ return self.category == VirtualVariableCategory.PARAMETER
264
+
265
+ @property
266
+ def was_tmp(self) -> bool:
267
+ return self.category == VirtualVariableCategory.TMP
268
+
269
+ @property
270
+ def reg_offset(self) -> int:
271
+ if self.was_reg:
272
+ assert isinstance(self.oident, int)
273
+ return self.oident
274
+ if self.was_parameter and self.parameter_category == VirtualVariableCategory.REGISTER:
275
+ return self.parameter_reg_offset # type: ignore
276
+ raise TypeError("Is not a register")
277
+
278
+ @property
279
+ def stack_offset(self) -> int:
280
+ if self.was_stack:
281
+ assert isinstance(self.oident, int)
282
+ return self.oident
283
+ if self.was_parameter and self.parameter_category == VirtualVariableCategory.STACK:
284
+ return self.parameter_stack_offset # type: ignore
285
+ raise TypeError("Is not a stack variable")
286
+
287
+ @property
288
+ def tmp_idx(self) -> int | None:
289
+ if self.was_tmp:
290
+ assert isinstance(self.oident, int)
291
+ return self.oident
292
+ return None
293
+
294
+ @property
295
+ def parameter_category(self) -> VirtualVariableCategory | None:
296
+ if self.was_parameter:
297
+ assert isinstance(self.oident, tuple)
298
+ return self.oident[0]
299
+ return None
300
+
301
+ @property
302
+ def parameter_reg_offset(self) -> int | None:
303
+ if self.was_parameter and self.parameter_category == VirtualVariableCategory.REGISTER:
304
+ assert isinstance(self.oident, tuple)
305
+ return self.oident[1]
306
+ return None
307
+
308
+ @property
309
+ def parameter_stack_offset(self) -> int | None:
310
+ if self.was_parameter and self.parameter_category == VirtualVariableCategory.STACK:
311
+ assert isinstance(self.oident, tuple)
312
+ return self.oident[1]
313
+ return None
314
+
315
+ def likes(self, other):
316
+ return (
317
+ isinstance(other, VirtualVariable)
318
+ and self.varid == other.varid
319
+ and self.bits == other.bits
320
+ and self.category == other.category
321
+ and self.oident == other.oident
322
+ )
323
+
324
+ def matches(self, other):
325
+ return (
326
+ isinstance(other, VirtualVariable)
327
+ and self.bits == other.bits
328
+ and self.category == other.category
329
+ and self.oident == other.oident
330
+ )
331
+
332
+ def __repr__(self):
333
+ ori_str = ""
334
+ match self.category:
335
+ case VirtualVariableCategory.REGISTER:
336
+ ori_str = f"{{reg {self.reg_offset}}}"
337
+ case VirtualVariableCategory.STACK:
338
+ ori_str = f"{{stack {self.oident}}}"
339
+ return f"vvar_{self.varid}{ori_str}"
340
+
341
+ __hash__ = TaggedObject.__hash__ # type: ignore
342
+
343
+ def _hash_core(self):
344
+ return stable_hash(("var", self.varid, self.bits, self.category, self.oident))
345
+
346
+ def copy(self) -> VirtualVariable:
347
+ return VirtualVariable(
348
+ self.idx,
349
+ self.varid,
350
+ self.bits,
351
+ self.category,
352
+ oident=self.oident,
353
+ variable=self.variable,
354
+ variable_offset=self.variable_offset,
355
+ **self.tags,
356
+ )
357
+
358
+
359
+ class Phi(Atom):
360
+
361
+ __slots__ = ("src_and_vvars",)
362
+
363
+ def __init__(
364
+ self,
365
+ idx,
366
+ bits,
367
+ src_and_vvars: list[tuple[tuple[int, int | None], VirtualVariable | None]],
368
+ **kwargs,
369
+ ):
370
+ super().__init__(idx, **kwargs)
371
+ self.bits = bits
372
+ self.src_and_vvars = src_and_vvars
373
+
374
+ @property
375
+ def size(self) -> int:
376
+ return self.bits // 8
377
+
378
+ @property
379
+ def op(self) -> str:
380
+ return "Phi"
381
+
382
+ @property
383
+ def verbose_op(self) -> str:
384
+ return "Phi"
385
+
386
+ def likes(self, other) -> bool:
387
+ if isinstance(other, Phi) and self.bits == other.bits:
388
+ self_src_and_vvarids = {(src, vvar.varid if vvar is not None else None) for src, vvar in self.src_and_vvars}
389
+ other_src_and_vvarids = {
390
+ (src, vvar.varid if vvar is not None else None) for src, vvar in other.src_and_vvars
391
+ }
392
+ return self_src_and_vvarids == other_src_and_vvarids
393
+ return False
394
+
395
+ def matches(self, other) -> bool:
396
+ if isinstance(other, Phi) and self.bits == other.bits:
397
+ if len(self.src_and_vvars) != len(other.src_and_vvars):
398
+ return False
399
+ self_src_and_vvars = dict(self.src_and_vvars)
400
+ other_src_and_vvars = dict(other.src_and_vvars)
401
+ for src, self_vvar in self_src_and_vvars.items():
402
+ if src not in other_src_and_vvars:
403
+ return False
404
+ other_vvar = other_src_and_vvars[src]
405
+ if self_vvar is None and other_vvar is None:
406
+ continue
407
+ if (
408
+ (self_vvar is None and other_vvar is not None)
409
+ or (self_vvar is not None and other_vvar is None)
410
+ or (self_vvar is not None and other_vvar is not None and not self_vvar.matches(other_vvar))
411
+ ):
412
+ return False
413
+ return True
414
+ return False
415
+
416
+ def __repr__(self):
417
+ return f"𝜙@{self.bits}b {self.src_and_vvars}"
418
+
419
+ __hash__ = TaggedObject.__hash__ # type: ignore
420
+
421
+ def _hash_core(self):
422
+ return stable_hash(("phi", self.bits, tuple(sorted(self.src_and_vvars, key=self._src_and_vvar_filter))))
423
+
424
+ def copy(self) -> Phi:
425
+ return Phi(
426
+ self.idx,
427
+ self.bits,
428
+ self.src_and_vvars[::],
429
+ variable=self.variable,
430
+ variable_offset=self.variable_offset,
431
+ **self.tags,
432
+ )
433
+
434
+ def replace(self, old_expr, new_expr):
435
+ replaced = False
436
+ new_src_and_vvars = []
437
+ for src, vvar in self.src_and_vvars:
438
+ if vvar == old_expr and isinstance(new_expr, VirtualVariable):
439
+ replaced = True
440
+ new_src_and_vvars.append((src, new_expr))
441
+ else:
442
+ new_src_and_vvars.append((src, vvar))
443
+
444
+ if replaced:
445
+ return True, Phi(
446
+ self.idx,
447
+ self.bits,
448
+ new_src_and_vvars,
449
+ variable=self.variable,
450
+ variable_offset=self.variable_offset,
451
+ **self.tags,
452
+ )
453
+ return False, self
454
+
455
+ @staticmethod
456
+ def _src_and_vvar_filter(
457
+ src_and_vvar: tuple[tuple[int, int | None], VirtualVariable | None],
458
+ ) -> tuple[tuple[int, int], int]:
459
+ src, vvar = src_and_vvar
460
+ if src[1] is None:
461
+ src = src[0], -1
462
+ vvar_id = vvar.varid if vvar is not None else -1
463
+ return src, vvar_id # type: ignore
464
+
465
+
466
+ class Op(Expression):
467
+ __slots__ = ("op",)
468
+
469
+ def __init__(self, idx, depth, op, **kwargs):
470
+ super().__init__(idx, depth, **kwargs)
471
+ self.op = op
472
+
473
+ @property
474
+ def verbose_op(self):
475
+ return self.op
476
+
477
+
478
+ class UnaryOp(Op):
479
+ __slots__ = (
480
+ "operand",
481
+ "variable",
482
+ "variable_offset",
483
+ )
484
+
485
+ def __init__(
486
+ self,
487
+ idx: int | None,
488
+ op: str,
489
+ operand: Expression,
490
+ variable=None,
491
+ variable_offset: int | None = None,
492
+ bits=None,
493
+ **kwargs,
494
+ ):
495
+ super().__init__(idx, (operand.depth if isinstance(operand, Expression) else 0) + 1, op, **kwargs)
496
+
497
+ self.operand = operand
498
+ self.bits = operand.bits if bits is None else bits
499
+ self.variable = variable
500
+ self.variable_offset = variable_offset
501
+
502
+ def __str__(self):
503
+ return f"({self.op} {self.operand!s})"
504
+
505
+ def __repr__(self):
506
+ return str(self)
507
+
508
+ def likes(self, other):
509
+ return (
510
+ type(other) is UnaryOp
511
+ and self.op == other.op
512
+ and self.bits == other.bits
513
+ and self.operand.likes(other.operand)
514
+ )
515
+
516
+ def matches(self, other):
517
+ return (
518
+ type(other) is UnaryOp
519
+ and self.op == other.op
520
+ and self.bits == other.bits
521
+ and self.operand.matches(other.operand)
522
+ )
523
+
524
+ __hash__ = TaggedObject.__hash__ # type: ignore
525
+
526
+ def _hash_core(self):
527
+ return stable_hash((self.op, self.operand, self.bits))
528
+
529
+ def replace(self, old_expr, new_expr):
530
+ if self.operand == old_expr:
531
+ r = True
532
+ replaced_operand = new_expr
533
+ else:
534
+ r, replaced_operand = self.operand.replace(old_expr, new_expr)
535
+
536
+ if r:
537
+ return True, UnaryOp(self.idx, self.op, replaced_operand, bits=self.bits, **self.tags)
538
+ return False, self
539
+
540
+ @property
541
+ def operands(self):
542
+ return [self.operand]
543
+
544
+ @property
545
+ def size(self):
546
+ return self.bits // 8
547
+
548
+ def copy(self) -> UnaryOp:
549
+ return UnaryOp(
550
+ self.idx,
551
+ self.op,
552
+ self.operand,
553
+ variable=self.variable,
554
+ variable_offset=self.variable_offset,
555
+ bits=self.bits,
556
+ **self.tags,
557
+ )
558
+
559
+ def has_atom(self, atom, identity=True):
560
+ if super().has_atom(atom, identity=identity):
561
+ return True
562
+ return self.operand.has_atom(atom, identity=identity)
563
+
564
+
565
+ class ConvertType(Enum):
566
+ TYPE_INT = 0
567
+ TYPE_FP = 1
568
+
569
+
570
+ class Convert(UnaryOp):
571
+ TYPE_INT = ConvertType.TYPE_INT
572
+ TYPE_FP = ConvertType.TYPE_FP
573
+
574
+ __slots__ = (
575
+ "from_bits",
576
+ "from_type",
577
+ "is_signed",
578
+ "rounding_mode",
579
+ "to_bits",
580
+ "to_type",
581
+ )
582
+
583
+ def __init__(
584
+ self,
585
+ idx: int | None,
586
+ from_bits: int,
587
+ to_bits: int,
588
+ is_signed: bool,
589
+ operand: Expression,
590
+ from_type: ConvertType = TYPE_INT,
591
+ to_type: ConvertType = TYPE_INT,
592
+ rounding_mode=None,
593
+ **kwargs,
594
+ ):
595
+ super().__init__(idx, "Convert", operand, **kwargs)
596
+
597
+ self.from_bits = from_bits
598
+ self.to_bits = to_bits
599
+ # override the size
600
+ self.bits = to_bits
601
+ self.is_signed = is_signed
602
+ self.from_type = from_type
603
+ self.to_type = to_type
604
+ self.rounding_mode = rounding_mode
605
+
606
+ def __str__(self):
607
+ return f"Conv({self.from_bits}->{'s' if self.is_signed else ''}{self.to_bits}, {self.operand})"
608
+
609
+ def __repr__(self):
610
+ return str(self)
611
+
612
+ def likes(self, other):
613
+ return (
614
+ type(other) is Convert
615
+ and self.from_bits == other.from_bits
616
+ and self.to_bits == other.to_bits
617
+ and self.bits == other.bits
618
+ and self.is_signed == other.is_signed
619
+ and self.operand.likes(other.operand)
620
+ and self.from_type == other.from_type
621
+ and self.to_type == other.to_type
622
+ and self.rounding_mode == other.rounding_mode
623
+ )
624
+
625
+ def matches(self, other):
626
+ return (
627
+ type(other) is Convert
628
+ and self.from_bits == other.from_bits
629
+ and self.to_bits == other.to_bits
630
+ and self.bits == other.bits
631
+ and self.is_signed == other.is_signed
632
+ and self.operand.matches(other.operand)
633
+ and self.from_type == other.from_type
634
+ and self.to_type == other.to_type
635
+ and self.rounding_mode == other.rounding_mode
636
+ )
637
+
638
+ __hash__ = TaggedObject.__hash__ # type: ignore
639
+
640
+ def _hash_core(self):
641
+ return stable_hash(
642
+ (
643
+ self.operand,
644
+ self.from_bits,
645
+ self.to_bits,
646
+ self.bits,
647
+ self.is_signed,
648
+ self.from_type,
649
+ self.to_type,
650
+ self.rounding_mode,
651
+ )
652
+ )
653
+
654
+ def replace(self, old_expr, new_expr):
655
+ if self.operand == old_expr:
656
+ r0 = True
657
+ replaced_operand = new_expr
658
+ else:
659
+ r0, replaced_operand = self.operand.replace(old_expr, new_expr)
660
+
661
+ if self.rounding_mode is not None:
662
+ if self.rounding_mode.likes(old_expr):
663
+ r1 = True
664
+ replaced_rm = new_expr
665
+ else:
666
+ r1, replaced_rm = self.rounding_mode.replace(old_expr, new_expr)
667
+ else:
668
+ r1 = False
669
+ replaced_rm = None
670
+
671
+ if r0 or r1:
672
+ return True, Convert(
673
+ self.idx,
674
+ self.from_bits,
675
+ self.to_bits,
676
+ self.is_signed,
677
+ replaced_operand if replaced_operand is not None else self.operand,
678
+ from_type=self.from_type,
679
+ to_type=self.to_type,
680
+ rounding_mode=replaced_rm if replaced_rm is not None else self.rounding_mode,
681
+ **self.tags,
682
+ )
683
+ return False, self
684
+
685
+ def copy(self) -> Convert:
686
+ return Convert(
687
+ self.idx,
688
+ self.from_bits,
689
+ self.to_bits,
690
+ self.is_signed,
691
+ self.operand,
692
+ from_type=self.from_type,
693
+ to_type=self.to_type,
694
+ rounding_mode=self.rounding_mode,
695
+ **self.tags,
696
+ )
697
+
698
+
699
+ class Reinterpret(UnaryOp):
700
+ __slots__ = (
701
+ "from_bits",
702
+ "from_type",
703
+ "to_bits",
704
+ "to_type",
705
+ )
706
+
707
+ def __init__(self, idx, from_bits: int, from_type: str, to_bits: int, to_type: str, operand, **kwargs):
708
+ super().__init__(idx, "Reinterpret", operand, **kwargs)
709
+
710
+ assert (from_type == "I" and to_type == "F") or (from_type == "F" and to_type == "I")
711
+
712
+ self.from_bits = from_bits
713
+ self.from_type = from_type
714
+ self.to_bits = to_bits
715
+ self.to_type = to_type
716
+
717
+ self.bits = self.to_bits
718
+
719
+ def __str__(self):
720
+ return f"Reinterpret({self.from_type}{self.from_bits}->{self.to_type}{self.to_bits}, {self.operand})"
721
+
722
+ def __repr__(self):
723
+ return str(self)
724
+
725
+ def likes(self, other):
726
+ return (
727
+ type(other) is Reinterpret
728
+ and self.from_bits == other.from_bits
729
+ and self.from_type == other.from_type
730
+ and self.to_bits == other.to_bits
731
+ and self.to_type == other.to_type
732
+ and self.operand.likes(other.operand)
733
+ )
734
+
735
+ def matches(self, other):
736
+ return (
737
+ type(other) is Reinterpret
738
+ and self.from_bits == other.from_bits
739
+ and self.from_type == other.from_type
740
+ and self.to_bits == other.to_bits
741
+ and self.to_type == other.to_type
742
+ and self.operand.matches(other.operand)
743
+ )
744
+
745
+ __hash__ = TaggedObject.__hash__ # type: ignore
746
+
747
+ def _hash_core(self):
748
+ return stable_hash(
749
+ (
750
+ self.operand,
751
+ self.from_bits,
752
+ self.from_type,
753
+ self.to_bits,
754
+ self.to_type,
755
+ )
756
+ )
757
+
758
+ def replace(self, old_expr, new_expr):
759
+ if self.operand == old_expr:
760
+ r = True
761
+ replaced_operand = new_expr
762
+ else:
763
+ r, replaced_operand = self.operand.replace(old_expr, new_expr)
764
+
765
+ if r:
766
+ return True, Reinterpret(
767
+ self.idx, self.from_bits, self.from_type, self.to_bits, self.to_type, replaced_operand, **self.tags
768
+ )
769
+ return False, self
770
+
771
+ def copy(self) -> Reinterpret:
772
+ return Reinterpret(
773
+ self.idx, self.from_bits, self.from_type, self.to_bits, self.to_type, self.operand, **self.tags
774
+ )
775
+
776
+
777
+ class BinaryOp(Op):
778
+ __slots__ = (
779
+ "floating_point",
780
+ "operands",
781
+ "rounding_mode",
782
+ "signed",
783
+ "variable",
784
+ "variable_offset",
785
+ "vector_count",
786
+ "vector_size",
787
+ )
788
+
789
+ OPSTR_MAP = {
790
+ "Add": "+",
791
+ "AddF": "+",
792
+ "AddV": "+",
793
+ "Sub": "-",
794
+ "SubF": "-",
795
+ "Mul": "*",
796
+ "MulF": "*",
797
+ "MulV": "*",
798
+ "Div": "/",
799
+ "DivF": "/",
800
+ "Mod": "%",
801
+ "Xor": "^",
802
+ "And": "&",
803
+ "LogicalAnd": "&&",
804
+ "Or": "|",
805
+ "LogicalOr": "||",
806
+ "Shl": "<<",
807
+ "Shr": ">>",
808
+ "Sar": ">>a",
809
+ "CmpF": "CmpF",
810
+ "CmpEQ": "==",
811
+ "CmpNE": "!=",
812
+ "CmpLT": "<",
813
+ "CmpLE": "<=",
814
+ "CmpGT": ">",
815
+ "CmpGE": ">=",
816
+ "CmpLT (signed)": "<s",
817
+ "CmpLE (signed)": "<=s",
818
+ "CmpGT (signed)": ">s",
819
+ "CmpGE (signed)": ">=s",
820
+ "Concat": "CONCAT",
821
+ "Ror": "ROR",
822
+ "Rol": "ROL",
823
+ "Carry": "CARRY",
824
+ "SCarry": "SCARRY",
825
+ "SBorrow": "SBORROW",
826
+ }
827
+
828
+ COMPARISON_NEGATION = {
829
+ "CmpEQ": "CmpNE",
830
+ "CmpNE": "CmpEQ",
831
+ "CmpLT": "CmpGE",
832
+ "CmpGE": "CmpLT",
833
+ "CmpLE": "CmpGT",
834
+ "CmpGT": "CmpLE",
835
+ }
836
+
837
+ def __init__(
838
+ self,
839
+ idx: int | None,
840
+ op: str,
841
+ operands: Sequence[Expression],
842
+ signed: bool = False,
843
+ *,
844
+ variable=None,
845
+ variable_offset=None,
846
+ bits=None,
847
+ floating_point=False,
848
+ rounding_mode=None,
849
+ vector_count: int | None = None,
850
+ vector_size: int | None = None,
851
+ **kwargs,
852
+ ):
853
+ depth = (
854
+ max(
855
+ operands[0].depth if isinstance(operands[0], Expression) else 0,
856
+ operands[1].depth if isinstance(operands[1], Expression) else 0,
857
+ )
858
+ + 1
859
+ )
860
+
861
+ super().__init__(idx, depth, op, **kwargs)
862
+
863
+ assert len(operands) == 2
864
+ self.operands = operands
865
+
866
+ if bits is not None:
867
+ self.bits = bits
868
+ elif self.op == "CmpF":
869
+ self.bits = 32 # floating point comparison
870
+ elif self.op in {
871
+ "CmpEQ",
872
+ "CmpNE",
873
+ "CmpLT",
874
+ "CmpGE",
875
+ "CmpLE",
876
+ "CmpGT",
877
+ "ExpCmpNE",
878
+ }:
879
+ self.bits = 1
880
+ elif self.op in {"Carry", "SCarry", "SBorrow"}:
881
+ self.bits = 8
882
+ elif self.op == "Concat":
883
+ self.bits = get_bits(operands[0]) + get_bits(operands[1])
884
+ elif self.op == "Mull":
885
+ self.bits = get_bits(operands[0]) * 2 if not isinstance(operands[0], int) else get_bits(operands[1]) * 2
886
+ else:
887
+ self.bits = get_bits(operands[0]) if not isinstance(operands[0], int) else get_bits(operands[1])
888
+ self.signed = signed
889
+ self.variable = variable
890
+ self.variable_offset = variable_offset
891
+ self.floating_point = floating_point
892
+ self.rounding_mode: str | None = rounding_mode
893
+ self.vector_count = vector_count
894
+ self.vector_size = vector_size
895
+
896
+ # TODO: sanity check of operands' sizes for some ops
897
+ # assert self.bits == operands[1].bits
898
+
899
+ def __str__(self):
900
+ op_str = self.OPSTR_MAP.get(self.verbose_op, self.verbose_op)
901
+ return f"({self.operands[0]!s} {op_str} {self.operands[1]!s})"
902
+
903
+ def __repr__(self):
904
+ return f"{self.verbose_op}({self.operands[0]}, {self.operands[1]})"
905
+
906
+ def likes(self, other):
907
+ return (
908
+ type(other) is BinaryOp
909
+ and self.op == other.op
910
+ and self.bits == other.bits
911
+ and self.signed == other.signed
912
+ and is_none_or_likeable(self.operands, other.operands, is_list=True)
913
+ and self.floating_point == other.floating_point
914
+ and self.rounding_mode == other.rounding_mode
915
+ )
916
+
917
+ def matches(self, other):
918
+ return (
919
+ type(other) is BinaryOp
920
+ and self.op == other.op
921
+ and self.bits == other.bits
922
+ and self.signed == other.signed
923
+ and is_none_or_matchable(self.operands, other.operands, is_list=True)
924
+ and self.floating_point == other.floating_point
925
+ and self.rounding_mode == other.rounding_mode
926
+ )
927
+
928
+ __hash__ = TaggedObject.__hash__ # type: ignore
929
+
930
+ def _hash_core(self):
931
+ return stable_hash(
932
+ (self.op, tuple(self.operands), self.bits, self.signed, self.floating_point, self.rounding_mode)
933
+ )
934
+
935
+ def has_atom(self, atom, identity=True):
936
+ if super().has_atom(atom, identity=identity):
937
+ return True
938
+
939
+ for op in self.operands:
940
+ if identity and op == atom:
941
+ return True
942
+ if not identity and isinstance(op, Expression) and op.likes(atom):
943
+ return True
944
+ if isinstance(op, Expression) and op.has_atom(atom, identity=identity):
945
+ return True
946
+
947
+ if self.rounding_mode is not None:
948
+ if identity and self.rounding_mode == atom:
949
+ return True
950
+ if not identity and isinstance(self.rounding_mode, Atom) and self.rounding_mode.likes(atom):
951
+ return True
952
+ if isinstance(self.rounding_mode, Atom) and self.rounding_mode.has_atom(atom, identity=identity):
953
+ return True
954
+
955
+ return False
956
+
957
+ def replace(self, old_expr: Expression, new_expr: Expression) -> tuple[bool, BinaryOp]:
958
+ if self.operands[0] == old_expr:
959
+ r0 = True
960
+ replaced_operand_0 = new_expr
961
+ elif isinstance(self.operands[0], Expression):
962
+ r0, replaced_operand_0 = self.operands[0].replace(old_expr, new_expr)
963
+ else:
964
+ r0, replaced_operand_0 = False, new_expr
965
+
966
+ if self.operands[1] == old_expr:
967
+ r1 = True
968
+ replaced_operand_1 = new_expr
969
+ elif isinstance(self.operands[1], Expression):
970
+ r1, replaced_operand_1 = self.operands[1].replace(old_expr, new_expr)
971
+ else:
972
+ r1, replaced_operand_1 = False, new_expr
973
+
974
+ r2, replaced_rm = False, None
975
+ if self.rounding_mode is not None and self.rounding_mode == old_expr:
976
+ r2 = True
977
+ replaced_rm = new_expr
978
+
979
+ if r0 or r1:
980
+ return True, BinaryOp(
981
+ self.idx,
982
+ self.op,
983
+ [replaced_operand_0 if r0 else self.operands[0], replaced_operand_1 if r1 else self.operands[1]],
984
+ signed=self.signed,
985
+ bits=self.bits,
986
+ floating_point=self.floating_point,
987
+ rounding_mode=replaced_rm if r2 else self.rounding_mode,
988
+ **self.tags,
989
+ )
990
+ return False, self
991
+
992
+ @property
993
+ def verbose_op(self):
994
+ op = self.op
995
+ if self.floating_point:
996
+ op += " (float)"
997
+ else:
998
+ if self.signed:
999
+ op += " (signed)"
1000
+ return op
1001
+
1002
+ @property
1003
+ def size(self):
1004
+ return self.bits // 8
1005
+
1006
+ def copy(self) -> BinaryOp:
1007
+ return BinaryOp(
1008
+ self.idx,
1009
+ self.op,
1010
+ self.operands[::],
1011
+ variable=self.variable,
1012
+ signed=self.signed,
1013
+ variable_offset=self.variable_offset,
1014
+ bits=self.bits,
1015
+ floating_point=self.floating_point,
1016
+ rounding_mode=self.rounding_mode,
1017
+ **self.tags,
1018
+ )
1019
+
1020
+
1021
+ class Load(Expression):
1022
+ __slots__ = (
1023
+ "addr",
1024
+ "alt",
1025
+ "endness",
1026
+ "guard",
1027
+ "size",
1028
+ "variable",
1029
+ "variable_offset",
1030
+ )
1031
+
1032
+ def __init__(
1033
+ self,
1034
+ idx: int | None,
1035
+ addr: Expression,
1036
+ size: int,
1037
+ endness: str,
1038
+ variable=None,
1039
+ variable_offset=None,
1040
+ guard=None,
1041
+ alt=None,
1042
+ **kwargs,
1043
+ ):
1044
+ depth = max(addr.depth, size.depth if isinstance(size, Expression) else 0) + 1
1045
+ super().__init__(idx, depth, **kwargs)
1046
+
1047
+ self.addr = addr
1048
+ self.size = size
1049
+ self.endness = endness
1050
+ self.guard = guard
1051
+ self.alt = alt
1052
+ self.variable = variable
1053
+ self.variable_offset = variable_offset
1054
+ self.bits = self.size * 8
1055
+
1056
+ def __repr__(self):
1057
+ return str(self)
1058
+
1059
+ def __str__(self):
1060
+ return f"Load(addr={self.addr}, size={self.size}, endness={self.endness})"
1061
+
1062
+ def has_atom(self, atom, identity=True):
1063
+ if super().has_atom(atom, identity=identity):
1064
+ return True
1065
+
1066
+ if claripy is not None and isinstance(self.addr, (int, claripy.ast.Base)):
1067
+ return False
1068
+ return self.addr.has_atom(atom, identity=identity)
1069
+
1070
+ def replace(self, old_expr, new_expr):
1071
+ if self.addr == old_expr:
1072
+ r = True
1073
+ replaced_addr = new_expr
1074
+ else:
1075
+ r, replaced_addr = self.addr.replace(old_expr, new_expr)
1076
+
1077
+ if r:
1078
+ return True, Load(self.idx, replaced_addr, self.size, self.endness, **self.tags)
1079
+ return False, self
1080
+
1081
+ def _likes_addr(self, other_addr):
1082
+ if hasattr(self.addr, "likes") and hasattr(other_addr, "likes"):
1083
+ return self.addr.likes(other_addr)
1084
+
1085
+ return self.addr == other_addr
1086
+
1087
+ def likes(self, other):
1088
+ return (
1089
+ type(other) is Load
1090
+ and self._likes_addr(other.addr)
1091
+ and self.size == other.size
1092
+ and self.endness == other.endness
1093
+ and self.guard == other.guard
1094
+ and self.alt == other.alt
1095
+ )
1096
+
1097
+ def _matches_addr(self, other_addr):
1098
+ if hasattr(self.addr, "matches") and hasattr(other_addr, "matches"):
1099
+ return self.addr.matches(other_addr)
1100
+ return self.addr == other_addr
1101
+
1102
+ def matches(self, other):
1103
+ return (
1104
+ type(other) is Load
1105
+ and self._matches_addr(other.addr)
1106
+ and self.size == other.size
1107
+ and self.endness == other.endness
1108
+ and self.guard == other.guard
1109
+ and self.alt == other.alt
1110
+ )
1111
+
1112
+ __hash__ = TaggedObject.__hash__ # type: ignore
1113
+
1114
+ def _hash_core(self):
1115
+ return stable_hash(("Load", self.addr, self.size, self.endness))
1116
+
1117
+ def copy(self) -> Load:
1118
+ return Load(
1119
+ self.idx,
1120
+ self.addr,
1121
+ self.size,
1122
+ self.endness,
1123
+ variable=self.variable,
1124
+ variable_offset=self.variable_offset,
1125
+ guard=self.guard,
1126
+ alt=self.alt,
1127
+ **self.tags,
1128
+ )
1129
+
1130
+
1131
+ class ITE(Expression):
1132
+ __slots__ = (
1133
+ "cond",
1134
+ "iffalse",
1135
+ "iftrue",
1136
+ "variable",
1137
+ "variable_offset",
1138
+ )
1139
+
1140
+ def __init__(
1141
+ self,
1142
+ idx: int | None,
1143
+ cond: Expression,
1144
+ iffalse: Expression,
1145
+ iftrue: Expression,
1146
+ variable=None,
1147
+ variable_offset=None,
1148
+ **kwargs,
1149
+ ):
1150
+ depth = (
1151
+ max(
1152
+ cond.depth if isinstance(cond, Expression) else 0,
1153
+ iffalse.depth if isinstance(iffalse, Expression) else 0,
1154
+ iftrue.depth if isinstance(iftrue, Expression) else 0,
1155
+ )
1156
+ + 1
1157
+ )
1158
+ super().__init__(idx, depth, **kwargs)
1159
+
1160
+ self.cond = cond
1161
+ self.iffalse = iffalse
1162
+ self.iftrue = iftrue
1163
+ self.bits = iftrue.bits
1164
+ self.variable = variable
1165
+ self.variable_offset = variable_offset
1166
+
1167
+ def __repr__(self):
1168
+ return str(self)
1169
+
1170
+ def __str__(self):
1171
+ return f"(({self.cond}) ? ({self.iftrue}) : ({self.iffalse}))"
1172
+
1173
+ def likes(self, other):
1174
+ return (
1175
+ type(other) is ITE
1176
+ and self.cond.likes(other.cond)
1177
+ and self.iffalse == other.iffalse
1178
+ and self.iftrue == other.iftrue
1179
+ and self.bits == other.bits
1180
+ )
1181
+
1182
+ def matches(self, other):
1183
+ return (
1184
+ type(other) is ITE
1185
+ and self.cond.matches(other.cond)
1186
+ and self.iffalse == other.iffalse
1187
+ and self.iftrue == other.iftrue
1188
+ and self.bits == other.bits
1189
+ )
1190
+
1191
+ __hash__ = TaggedObject.__hash__ # type: ignore
1192
+
1193
+ def _hash_core(self):
1194
+ return stable_hash((ITE, self.cond, self.iffalse, self.iftrue, self.bits))
1195
+
1196
+ def has_atom(self, atom, identity=True):
1197
+ if super().has_atom(atom, identity=identity):
1198
+ return True
1199
+
1200
+ return (
1201
+ self.cond.has_atom(atom, identity=identity)
1202
+ or self.iftrue.has_atom(atom, identity=identity)
1203
+ or self.iffalse.has_atom(atom, identity=identity)
1204
+ )
1205
+
1206
+ def replace(self, old_expr, new_expr):
1207
+ if self.cond == old_expr:
1208
+ cond_replaced = True
1209
+ new_cond = new_expr
1210
+ else:
1211
+ cond_replaced, new_cond = self.cond.replace(old_expr, new_expr)
1212
+
1213
+ if self.iffalse == old_expr:
1214
+ iffalse_replaced = True
1215
+ new_iffalse = new_expr
1216
+ else:
1217
+ iffalse_replaced, new_iffalse = self.iffalse.replace(old_expr, new_expr)
1218
+
1219
+ if self.iftrue == old_expr:
1220
+ iftrue_replaced = True
1221
+ new_iftrue = new_expr
1222
+ else:
1223
+ iftrue_replaced, new_iftrue = self.iftrue.replace(old_expr, new_expr)
1224
+
1225
+ replaced = cond_replaced or iftrue_replaced or iffalse_replaced
1226
+
1227
+ if replaced:
1228
+ return True, ITE(self.idx, new_cond, new_iffalse, new_iftrue, **self.tags)
1229
+ return False, self
1230
+
1231
+ @property
1232
+ def size(self):
1233
+ return self.bits // 8
1234
+
1235
+ def copy(self) -> ITE:
1236
+ return ITE(self.idx, self.cond, self.iffalse, self.iftrue, **self.tags)
1237
+
1238
+
1239
+ class DirtyExpression(Expression):
1240
+ __slots__ = (
1241
+ "callee",
1242
+ "guard",
1243
+ "maddr",
1244
+ "mfx",
1245
+ "msize",
1246
+ "operands",
1247
+ )
1248
+
1249
+ def __init__(
1250
+ self,
1251
+ idx,
1252
+ callee: str,
1253
+ operands: list[Expression],
1254
+ *,
1255
+ guard: Expression | None = None,
1256
+ mfx: str | None = None,
1257
+ maddr: Expression | None = None,
1258
+ msize: int | None = None,
1259
+ # TODO: fxstate (guest state effects) is not modeled yet
1260
+ bits: int,
1261
+ **kwargs,
1262
+ ):
1263
+ super().__init__(idx, 1, **kwargs)
1264
+
1265
+ self.callee = callee
1266
+ self.guard = guard
1267
+ self.operands = operands
1268
+ self.mfx = mfx
1269
+ self.maddr = maddr
1270
+ self.msize = msize
1271
+ self.bits = bits
1272
+
1273
+ @property
1274
+ def op(self) -> str:
1275
+ return self.callee
1276
+
1277
+ @property
1278
+ def verbose_op(self) -> str:
1279
+ return self.op
1280
+
1281
+ def likes(self, other):
1282
+ return (
1283
+ type(other) is DirtyExpression
1284
+ and other.callee == self.callee
1285
+ and is_none_or_likeable(other.guard, self.guard)
1286
+ and len(self.operands) == len(other.operands)
1287
+ and all(op1.likes(op2) for op1, op2 in zip(self.operands, other.operands))
1288
+ and other.mfx == self.mfx
1289
+ and is_none_or_likeable(other.maddr, self.maddr)
1290
+ and other.msize == self.msize
1291
+ and self.bits == other.bits
1292
+ )
1293
+
1294
+ def matches(self, other):
1295
+ return (
1296
+ type(other) is DirtyExpression
1297
+ and other.callee == self.callee
1298
+ and is_none_or_matchable(other.guard, self.guard)
1299
+ and len(self.operands) == len(other.operands)
1300
+ and all(op1.matches(op2) for op1, op2 in zip(self.operands, other.operands))
1301
+ and other.mfx == self.mfx
1302
+ and is_none_or_matchable(other.maddr, self.maddr)
1303
+ and other.msize == self.msize
1304
+ and self.bits == other.bits
1305
+ )
1306
+
1307
+ __hash__ = TaggedObject.__hash__ # type: ignore
1308
+
1309
+ def _hash_core(self):
1310
+ return stable_hash(
1311
+ (
1312
+ DirtyExpression,
1313
+ self.callee,
1314
+ self.guard,
1315
+ tuple(self.operands),
1316
+ self.mfx,
1317
+ self.maddr,
1318
+ self.msize,
1319
+ self.bits,
1320
+ )
1321
+ )
1322
+
1323
+ def __repr__(self):
1324
+ return f"[D] {self.callee}({', '.join(repr(op) for op in self.operands)})"
1325
+
1326
+ def __str__(self):
1327
+ return f"[D] {self.callee}({', '.join(repr(op) for op in self.operands)})"
1328
+
1329
+ def copy(self) -> DirtyExpression:
1330
+ return DirtyExpression(
1331
+ self.idx,
1332
+ self.callee,
1333
+ self.operands,
1334
+ guard=self.guard,
1335
+ mfx=self.mfx,
1336
+ maddr=self.maddr,
1337
+ msize=self.msize,
1338
+ bits=self.bits,
1339
+ **self.tags,
1340
+ )
1341
+
1342
+ def replace(self, old_expr: Expression, new_expr: Expression):
1343
+ new_operands = []
1344
+ replaced = False
1345
+ for op in self.operands:
1346
+ if old_expr == op:
1347
+ replaced = True
1348
+ new_operands.append(new_expr)
1349
+ else:
1350
+ r, new_op = op.replace(old_expr, new_expr)
1351
+ if r:
1352
+ replaced = True
1353
+ new_operands.append(new_op)
1354
+ else:
1355
+ new_operands.append(op)
1356
+
1357
+ if replaced:
1358
+ return True, DirtyExpression(
1359
+ self.idx,
1360
+ self.callee,
1361
+ new_operands,
1362
+ guard=self.guard,
1363
+ mfx=self.mfx,
1364
+ maddr=self.maddr,
1365
+ msize=self.msize,
1366
+ bits=self.bits,
1367
+ **self.tags,
1368
+ )
1369
+ return False, self
1370
+
1371
+ @property
1372
+ def size(self):
1373
+ if self.bits is None:
1374
+ return None
1375
+ return self.bits // 8
1376
+
1377
+
1378
+ class VEXCCallExpression(Expression):
1379
+ __slots__ = (
1380
+ "callee",
1381
+ "operands",
1382
+ )
1383
+
1384
+ def __init__(self, idx: int | None, callee: str, operands: tuple[Expression, ...], bits: int, **kwargs):
1385
+ super().__init__(idx, max(operand.depth for operand in operands), **kwargs)
1386
+ self.callee = callee
1387
+ self.operands = operands
1388
+ self.bits = bits
1389
+
1390
+ @property
1391
+ def op(self) -> str:
1392
+ return self.callee
1393
+
1394
+ @property
1395
+ def verbose_op(self) -> str:
1396
+ return self.op
1397
+
1398
+ def likes(self, other):
1399
+ return (
1400
+ type(other) is VEXCCallExpression
1401
+ and other.callee == self.callee
1402
+ and len(self.operands) == len(other.operands)
1403
+ and self.bits == other.bits
1404
+ and all(op1.likes(op2) for op1, op2 in zip(other.operands, self.operands))
1405
+ )
1406
+
1407
+ def matches(self, other):
1408
+ return (
1409
+ type(other) is VEXCCallExpression
1410
+ and other.callee == self.callee
1411
+ and len(self.operands) == len(other.operands)
1412
+ and self.bits == other.bits
1413
+ and all(op1.matches(op2) for op1, op2 in zip(other.operands, self.operands))
1414
+ )
1415
+
1416
+ __hash__ = TaggedObject.__hash__ # type: ignore
1417
+
1418
+ def _hash_core(self):
1419
+ return stable_hash((VEXCCallExpression, self.callee, self.bits, tuple(self.operands)))
1420
+
1421
+ def __repr__(self):
1422
+ return f"VEXCCallExpression [{self.callee}({', '.join(repr(op) for op in self.operands)})]"
1423
+
1424
+ def __str__(self):
1425
+ operands_str = ", ".join(repr(op) for op in self.operands)
1426
+ return f"{self.callee}({operands_str})"
1427
+
1428
+ def copy(self) -> VEXCCallExpression:
1429
+ return VEXCCallExpression(self.idx, self.callee, self.operands, bits=self.bits, **self.tags)
1430
+
1431
+ def replace(self, old_expr, new_expr):
1432
+ new_operands = []
1433
+ replaced = False
1434
+ for operand in self.operands:
1435
+ if operand is old_expr:
1436
+ new_operands.append(new_expr)
1437
+ replaced = True
1438
+ else:
1439
+ operand_replaced, new_operand = operand.replace(old_expr, new_expr)
1440
+ if operand_replaced:
1441
+ new_operands.append(new_operand)
1442
+ replaced = True
1443
+ else:
1444
+ new_operands.append(operand)
1445
+
1446
+ if replaced:
1447
+ return True, VEXCCallExpression(self.idx, self.callee, tuple(new_operands), bits=self.bits, **self.tags)
1448
+ return False, self
1449
+
1450
+ @property
1451
+ def size(self):
1452
+ if self.bits is None:
1453
+ return None
1454
+ return self.bits // 8
1455
+
1456
+
1457
+ class MultiStatementExpression(Expression):
1458
+ """
1459
+ For representing comma-separated statements and expression in C.
1460
+ """
1461
+
1462
+ __slots__ = (
1463
+ "expr",
1464
+ "stmts",
1465
+ )
1466
+
1467
+ def __init__(self, idx: int | None, stmts: list[Statement], expr: Expression, **kwargs):
1468
+ super().__init__(idx, expr.depth + 1, **kwargs)
1469
+ self.stmts = stmts
1470
+ self.expr = expr
1471
+ self.bits = self.expr.bits
1472
+
1473
+ __hash__ = TaggedObject.__hash__ # type: ignore
1474
+
1475
+ def _hash_core(self):
1476
+ return stable_hash((MultiStatementExpression, *tuple(self.stmts), self.expr))
1477
+
1478
+ def likes(self, other):
1479
+ return (
1480
+ type(self) is type(other)
1481
+ and len(self.stmts) == len(other.stmts)
1482
+ and all(s_stmt.likes(o_stmt) for s_stmt, o_stmt in zip(self.stmts, other.stmts))
1483
+ and self.expr.likes(other.expr)
1484
+ )
1485
+
1486
+ def matches(self, other):
1487
+ return (
1488
+ type(self) is type(other)
1489
+ and len(self.stmts) == len(other.stmts)
1490
+ and all(s_stmt.matches(o_stmt) for s_stmt, o_stmt in zip(self.stmts, other.stmts))
1491
+ and self.expr.matches(other.expr)
1492
+ )
1493
+
1494
+ def __repr__(self):
1495
+ return f"MultiStatementExpression({self.stmts}, {self.expr})"
1496
+
1497
+ def __str__(self):
1498
+ stmts_str = [str(stmt) for stmt in self.stmts]
1499
+ expr_str = str(self.expr)
1500
+ concatenated_str = ", ".join([*stmts_str, expr_str])
1501
+ return f"({concatenated_str})"
1502
+
1503
+ @property
1504
+ def size(self):
1505
+ return self.expr.size
1506
+
1507
+ def replace(self, old_expr, new_expr):
1508
+ replaced = False
1509
+
1510
+ new_stmts = []
1511
+ for stmt in self.stmts:
1512
+ r, new_stmt = stmt.replace(old_expr, new_expr)
1513
+ new_stmts.append(new_stmt if new_stmt is not None else stmt)
1514
+ replaced |= r
1515
+
1516
+ if self.expr is old_expr:
1517
+ replaced = True
1518
+ new_expr_ = new_expr
1519
+ else:
1520
+ r, new_expr_ = self.expr.replace(old_expr, new_expr)
1521
+ replaced |= r
1522
+
1523
+ if replaced:
1524
+ return True, MultiStatementExpression(
1525
+ self.idx, new_stmts, new_expr_ if new_expr_ is not None else self.expr, **self.tags
1526
+ )
1527
+ return False, self
1528
+
1529
+ def copy(self) -> MultiStatementExpression:
1530
+ return MultiStatementExpression(self.idx, self.stmts[::], self.expr, **self.tags)
1531
+
1532
+
1533
+ #
1534
+ # Special (Dummy) expressions
1535
+ #
1536
+
1537
+
1538
+ class BasePointerOffset(Expression):
1539
+ __slots__ = (
1540
+ "base",
1541
+ "offset",
1542
+ "variable",
1543
+ "variable_offset",
1544
+ )
1545
+
1546
+ def __init__(
1547
+ self,
1548
+ idx: int | None,
1549
+ bits: int,
1550
+ base: Expression | str,
1551
+ offset: int,
1552
+ variable=None,
1553
+ variable_offset=None,
1554
+ **kwargs,
1555
+ ):
1556
+ super().__init__(idx, (offset.depth if isinstance(offset, Expression) else 0) + 1, **kwargs)
1557
+ self.bits = bits
1558
+ self.base = base
1559
+ self.offset = offset
1560
+ self.variable = variable
1561
+ self.variable_offset = variable_offset
1562
+
1563
+ @property
1564
+ def size(self):
1565
+ return self.bits // 8
1566
+
1567
+ def __repr__(self):
1568
+ if self.offset is None:
1569
+ return f"BaseOffset({self.base})"
1570
+ return f"BaseOffset({self.base}, {self.offset})"
1571
+
1572
+ def __str__(self):
1573
+ if self.offset is None:
1574
+ return str(self.base)
1575
+ if isinstance(self.offset, int):
1576
+ return f"{self.base}{self.offset:+d}"
1577
+ return f"{self.base}+{self.offset}"
1578
+
1579
+ def likes(self, other):
1580
+ return (
1581
+ type(other) is type(self)
1582
+ and self.bits == other.bits
1583
+ and self.base == other.base
1584
+ and self.offset == other.offset
1585
+ )
1586
+
1587
+ matches = likes
1588
+ __hash__ = TaggedObject.__hash__ # type: ignore
1589
+
1590
+ def _hash_core(self):
1591
+ return stable_hash((self.bits, self.base, self.offset))
1592
+
1593
+ def replace(self, old_expr, new_expr):
1594
+ if isinstance(self.base, Expression):
1595
+ base_replaced, new_base = self.base.replace(old_expr, new_expr)
1596
+ else:
1597
+ base_replaced, new_base = False, self.base
1598
+ if isinstance(self.offset, Expression):
1599
+ offset_replaced, new_offset = self.offset.replace(old_expr, new_expr)
1600
+ else:
1601
+ offset_replaced, new_offset = False, self.offset
1602
+
1603
+ if base_replaced or offset_replaced:
1604
+ return True, BasePointerOffset(self.idx, self.bits, new_base, new_offset, **self.tags)
1605
+ return False, self
1606
+
1607
+ def copy(self) -> BasePointerOffset:
1608
+ return BasePointerOffset(self.idx, self.bits, self.base, self.offset, **self.tags)
1609
+
1610
+
1611
+ class StackBaseOffset(BasePointerOffset):
1612
+ __slots__ = ()
1613
+
1614
+ def __init__(self, idx: int | None, bits: int, offset: int, **kwargs):
1615
+ # stack base offset is always signed
1616
+ if offset >= (1 << (bits - 1)):
1617
+ offset -= 1 << bits
1618
+ super().__init__(idx, bits, "stack_base", offset, **kwargs)
1619
+
1620
+ def copy(self) -> StackBaseOffset:
1621
+ return StackBaseOffset(self.idx, self.bits, self.offset, **self.tags)
1622
+
1623
+
1624
+ def negate(expr: Expression) -> Expression:
1625
+ if isinstance(expr, UnaryOp) and expr.op == "Not":
1626
+ # unpack
1627
+ return expr.operand
1628
+ if isinstance(expr, BinaryOp) and expr.op in BinaryOp.COMPARISON_NEGATION:
1629
+ return BinaryOp(
1630
+ expr.idx,
1631
+ BinaryOp.COMPARISON_NEGATION[expr.op],
1632
+ expr.operands,
1633
+ signed=expr.signed,
1634
+ bits=expr.bits,
1635
+ floating_point=expr.floating_point,
1636
+ rounding_mode=expr.rounding_mode,
1637
+ **expr.tags,
1638
+ )
1639
+ return UnaryOp(None, "Not", expr, **expr.tags)