angr 9.2.158__cp310-abi3-macosx_11_0_arm64.whl → 9.2.159__cp310-abi3-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/ailment/__init__.py +81 -0
- angr/ailment/block.py +81 -0
- angr/ailment/block_walker.py +845 -0
- angr/ailment/constant.py +3 -0
- angr/ailment/converter_common.py +11 -0
- angr/ailment/converter_pcode.py +623 -0
- angr/ailment/converter_vex.py +798 -0
- angr/ailment/expression.py +1639 -0
- angr/ailment/manager.py +33 -0
- angr/ailment/statement.py +978 -0
- angr/ailment/tagged_object.py +61 -0
- angr/ailment/utils.py +114 -0
- angr/analyses/calling_convention/calling_convention.py +6 -2
- angr/analyses/decompiler/ail_simplifier.py +5 -5
- angr/analyses/decompiler/block_io_finder.py +4 -4
- angr/analyses/decompiler/block_similarity.py +2 -2
- angr/analyses/decompiler/block_simplifier.py +4 -4
- angr/analyses/decompiler/callsite_maker.py +2 -2
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
- angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +1 -1
- angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +1 -1
- angr/analyses/decompiler/clinic.py +1 -1
- angr/analyses/decompiler/condition_processor.py +1 -1
- angr/analyses/decompiler/counters/boolean_counter.py +4 -4
- angr/analyses/decompiler/counters/call_counter.py +4 -4
- angr/analyses/decompiler/counters/expression_counters.py +5 -5
- angr/analyses/decompiler/counters/seq_cf_structure_counter.py +1 -1
- angr/analyses/decompiler/decompiler.py +5 -3
- angr/analyses/decompiler/dephication/dephication_base.py +12 -1
- angr/analyses/decompiler/dephication/graph_dephication.py +12 -5
- angr/analyses/decompiler/dephication/graph_rewriting.py +6 -10
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +109 -72
- angr/analyses/decompiler/dephication/rewriting_engine.py +32 -9
- angr/analyses/decompiler/dephication/seqnode_dephication.py +32 -10
- angr/analyses/decompiler/empty_node_remover.py +2 -2
- angr/analyses/decompiler/expression_narrower.py +6 -6
- angr/analyses/decompiler/goto_manager.py +2 -2
- angr/analyses/decompiler/jump_target_collector.py +1 -1
- angr/analyses/decompiler/label_collector.py +1 -1
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +25 -25
- angr/analyses/decompiler/optimization_passes/call_stmt_rewriter.py +1 -1
- angr/analyses/decompiler/optimization_passes/code_motion.py +2 -2
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +3 -3
- angr/analyses/decompiler/optimization_passes/const_derefs.py +3 -3
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +4 -4
- angr/analyses/decompiler/optimization_passes/deadblock_remover.py +2 -2
- angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +3 -3
- angr/analyses/decompiler/optimization_passes/div_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
- angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +4 -4
- angr/analyses/decompiler/optimization_passes/duplication_reverter/similarity.py +1 -1
- angr/analyses/decompiler/optimization_passes/duplication_reverter/utils.py +4 -4
- angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +3 -3
- angr/analyses/decompiler/optimization_passes/engine_base.py +1 -1
- angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +3 -3
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +2 -2
- angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +3 -3
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +4 -4
- angr/analyses/decompiler/optimization_passes/mod_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +25 -1
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +2 -2
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +4 -4
- angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -2
- angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +3 -3
- angr/analyses/decompiler/optimization_passes/switch_reused_entry_rewriter.py +3 -3
- angr/analyses/decompiler/optimization_passes/tag_slicer.py +1 -1
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_div_const_add_a_mul_n_div_const.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_mul_const_div_shr_const.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_div.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_sub_n.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/arm_cmpf.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/base.py +3 -3
- angr/analyses/decompiler/peephole_optimizations/basepointeroffset_add_n.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/basepointeroffset_and_mask.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/bitwise_or_to_logical_or.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/bool_expr_xor_1.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/bswap.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/coalesce_adjacent_shrs.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/coalesce_same_cascading_ifs.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/conv_a_sub0_shr_and.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/conv_shl_shr.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/extended_byte_and_mask.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/invert_negated_logical_conjuction_disjunction.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/one_sub_bool.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/remove_empty_if_body.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_branch.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_comparisons.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_reinterprets.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +3 -3
- angr/analyses/decompiler/peephole_optimizations/rewrite_mips_gp_loads.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/rol_ror.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/single_bit_cond_to_boolexpr.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/single_bit_xor.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/utils.py +1 -1
- angr/analyses/decompiler/redundant_label_remover.py +1 -1
- angr/analyses/decompiler/region_identifier.py +4 -4
- angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +1 -1
- angr/analyses/decompiler/region_simplifiers/cascading_ifs.py +1 -1
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +37 -8
- angr/analyses/decompiler/region_simplifiers/goto.py +1 -1
- angr/analyses/decompiler/region_simplifiers/if_.py +1 -1
- angr/analyses/decompiler/region_simplifiers/loop.py +1 -1
- angr/analyses/decompiler/region_simplifiers/node_address_finder.py +1 -1
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +14 -2
- angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +3 -3
- angr/analyses/decompiler/region_simplifiers/switch_expr_simplifier.py +1 -1
- angr/analyses/decompiler/return_maker.py +1 -1
- angr/analyses/decompiler/seq_to_blocks.py +1 -1
- angr/analyses/decompiler/sequence_walker.py +2 -2
- angr/analyses/decompiler/ssailification/rewriting.py +4 -4
- angr/analyses/decompiler/ssailification/rewriting_engine.py +4 -4
- angr/analyses/decompiler/ssailification/rewriting_state.py +3 -3
- angr/analyses/decompiler/ssailification/ssailification.py +2 -2
- angr/analyses/decompiler/ssailification/traversal.py +1 -1
- angr/analyses/decompiler/ssailification/traversal_engine.py +11 -2
- angr/analyses/decompiler/structured_codegen/c.py +3 -3
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +3 -3
- angr/analyses/decompiler/structuring/structurer_base.py +1 -1
- angr/analyses/decompiler/structuring/structurer_nodes.py +1 -2
- angr/analyses/decompiler/utils.py +1 -1
- angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
- angr/analyses/deobfuscator/string_obf_opt_passes.py +3 -3
- angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +2 -2
- angr/analyses/propagator/propagator.py +1 -1
- angr/analyses/proximity_graph.py +2 -2
- angr/analyses/reaching_definitions/engine_ail.py +1 -1
- angr/analyses/reaching_definitions/reaching_definitions.py +1 -1
- angr/analyses/reaching_definitions/subject.py +1 -1
- angr/analyses/s_liveness.py +2 -2
- angr/analyses/s_propagator.py +3 -3
- angr/analyses/s_reaching_definitions/s_rda_model.py +1 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +3 -3
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -3
- angr/analyses/variable_recovery/engine_ail.py +2 -2
- angr/analyses/variable_recovery/engine_base.py +1 -1
- angr/analyses/variable_recovery/variable_recovery_base.py +1 -1
- angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
- angr/engines/light/data.py +1 -1
- angr/engines/light/engine.py +1 -1
- angr/knowledge_plugins/key_definitions/atoms.py +1 -1
- angr/knowledge_plugins/propagations/prop_value.py +1 -1
- angr/knowledge_plugins/propagations/propagation_model.py +1 -1
- angr/knowledge_plugins/propagations/states.py +1 -1
- angr/knowledge_plugins/variables/variable_manager.py +1 -1
- angr/lib/angr_native.dylib +0 -0
- angr/rustylib.abi3.so +0 -0
- angr/utils/ail.py +4 -4
- angr/utils/endness.py +1 -1
- angr/utils/ssa/__init__.py +14 -4
- angr/utils/ssa/tmp_uses_collector.py +4 -4
- angr/utils/ssa/vvar_uses_collector.py +4 -4
- {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/METADATA +6 -6
- {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/RECORD +192 -180
- {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/WHEEL +0 -0
- {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/entry_points.txt +0 -0
- {angr-9.2.158.dist-info → angr-9.2.159.dist-info}/licenses/LICENSE +0 -0
- {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)
|