angr 9.2.158__cp310-abi3-manylinux2014_x86_64.whl → 9.2.160__cp310-abi3-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- 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 +5 -2
- 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 +17 -12
- 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/typehoon/simple_solver.py +231 -29
- angr/analyses/typehoon/typehoon.py +10 -2
- angr/analyses/variable_recovery/engine_ail.py +10 -22
- 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/rustylib.abi3.so +0 -0
- angr/state_plugins/unicorn_engine.py +4 -4
- 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.160.dist-info}/METADATA +6 -7
- {angr-9.2.158.dist-info → angr-9.2.160.dist-info}/RECORD +195 -183
- /angr/{lib/angr_native.so → unicornlib.so} +0 -0
- {angr-9.2.158.dist-info → angr-9.2.160.dist-info}/WHEEL +0 -0
- {angr-9.2.158.dist-info → angr-9.2.160.dist-info}/entry_points.txt +0 -0
- {angr-9.2.158.dist-info → angr-9.2.160.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.158.dist-info → angr-9.2.160.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
# pylint:disable=isinstance-second-argument-not-valid-type,no-self-use,arguments-renamed,too-many-boolean-expressions
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import claripy
|
|
10
|
+
except ImportError:
|
|
11
|
+
claripy = None
|
|
12
|
+
|
|
13
|
+
from .utils import stable_hash, is_none_or_likeable, is_none_or_matchable
|
|
14
|
+
from .tagged_object import TaggedObject
|
|
15
|
+
from .expression import Atom, Expression, DirtyExpression
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from angr.calling_conventions import SimCC
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Statement(TaggedObject, ABC):
|
|
22
|
+
"""
|
|
23
|
+
The base class of all AIL statements.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__slots__ = ()
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def __repr__(self):
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def __str__(self):
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def replace(self, old_expr: Expression, new_expr: Expression) -> tuple[bool, Self]:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
def eq(self, expr0, expr1): # pylint:disable=no-self-use
|
|
41
|
+
if claripy is not None and (isinstance(expr0, claripy.ast.Base) or isinstance(expr1, claripy.ast.Base)):
|
|
42
|
+
return expr0 is expr1
|
|
43
|
+
return expr0 == expr1
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def likes(self, other) -> bool: # pylint:disable=unused-argument,no-self-use
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def matches(self, other) -> bool: # pylint:disable=unused-argument,no-self-use
|
|
51
|
+
raise NotImplementedError
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Assignment(Statement):
|
|
55
|
+
"""
|
|
56
|
+
Assignment statement: expr_a = expr_b
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
__slots__ = (
|
|
60
|
+
"dst",
|
|
61
|
+
"src",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def __init__(self, idx: int | None, dst: Atom, src: Expression, **kwargs):
|
|
65
|
+
super().__init__(idx, **kwargs)
|
|
66
|
+
|
|
67
|
+
self.dst = dst
|
|
68
|
+
self.src = src
|
|
69
|
+
|
|
70
|
+
def __eq__(self, other):
|
|
71
|
+
return type(other) is Assignment and self.idx == other.idx and self.dst == other.dst and self.src == other.src
|
|
72
|
+
|
|
73
|
+
def likes(self, other):
|
|
74
|
+
return type(other) is Assignment and self.dst.likes(other.dst) and self.src.likes(other.src)
|
|
75
|
+
|
|
76
|
+
def matches(self, other):
|
|
77
|
+
return type(other) is Assignment and self.dst.matches(other.dst) and self.src.matches(other.src)
|
|
78
|
+
|
|
79
|
+
__hash__ = TaggedObject.__hash__
|
|
80
|
+
|
|
81
|
+
def _hash_core(self):
|
|
82
|
+
return stable_hash((Assignment, self.idx, self.dst, self.src))
|
|
83
|
+
|
|
84
|
+
def __repr__(self):
|
|
85
|
+
return f"Assignment ({self.dst}, {self.src})"
|
|
86
|
+
|
|
87
|
+
def __str__(self):
|
|
88
|
+
return f"{self.dst!s} = {self.src!s}"
|
|
89
|
+
|
|
90
|
+
def replace(self, old_expr: Expression, new_expr: Expression):
|
|
91
|
+
if self.dst == old_expr:
|
|
92
|
+
r_dst = True
|
|
93
|
+
assert isinstance(new_expr, Atom)
|
|
94
|
+
replaced_dst = new_expr
|
|
95
|
+
else:
|
|
96
|
+
r_dst, replaced_dst = self.dst.replace(old_expr, new_expr)
|
|
97
|
+
|
|
98
|
+
if self.src == old_expr:
|
|
99
|
+
r_src = True
|
|
100
|
+
replaced_src = new_expr
|
|
101
|
+
else:
|
|
102
|
+
r_src, replaced_src = self.src.replace(old_expr, new_expr)
|
|
103
|
+
|
|
104
|
+
if r_dst or r_src:
|
|
105
|
+
return True, Assignment(self.idx, replaced_dst, replaced_src, **self.tags)
|
|
106
|
+
return False, self
|
|
107
|
+
|
|
108
|
+
def copy(self) -> Assignment:
|
|
109
|
+
return Assignment(self.idx, self.dst, self.src, **self.tags)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class WeakAssignment(Statement):
|
|
113
|
+
"""
|
|
114
|
+
An assignment statement that does not create a new variable at its destination; It should be seen as
|
|
115
|
+
operator=(&dst, &src) in C++-like syntax.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
__slots__ = (
|
|
119
|
+
"dst",
|
|
120
|
+
"src",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def __init__(self, idx: int | None, dst: Atom, src: Expression, **kwargs):
|
|
124
|
+
super().__init__(idx, **kwargs)
|
|
125
|
+
|
|
126
|
+
self.dst = dst
|
|
127
|
+
self.src = src
|
|
128
|
+
|
|
129
|
+
def __eq__(self, other):
|
|
130
|
+
return (
|
|
131
|
+
type(other) is WeakAssignment and self.idx == other.idx and self.dst == other.dst and self.src == other.src
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def likes(self, other):
|
|
135
|
+
return type(other) is WeakAssignment and self.dst.likes(other.dst) and self.src.likes(other.src)
|
|
136
|
+
|
|
137
|
+
def matches(self, other):
|
|
138
|
+
return type(other) is WeakAssignment and self.dst.matches(other.dst) and self.src.matches(other.src)
|
|
139
|
+
|
|
140
|
+
__hash__ = TaggedObject.__hash__
|
|
141
|
+
|
|
142
|
+
def _hash_core(self):
|
|
143
|
+
return stable_hash((WeakAssignment, self.idx, self.dst, self.src))
|
|
144
|
+
|
|
145
|
+
def __repr__(self):
|
|
146
|
+
return f"WeakAssignment ({self.dst}, {self.src})"
|
|
147
|
+
|
|
148
|
+
def __str__(self):
|
|
149
|
+
return f"{self.dst!s} =W {self.src!s}"
|
|
150
|
+
|
|
151
|
+
def replace(self, old_expr: Expression, new_expr: Expression):
|
|
152
|
+
if self.dst == old_expr:
|
|
153
|
+
r_dst = True
|
|
154
|
+
assert isinstance(new_expr, Atom)
|
|
155
|
+
replaced_dst = new_expr
|
|
156
|
+
else:
|
|
157
|
+
r_dst, replaced_dst = self.dst.replace(old_expr, new_expr)
|
|
158
|
+
|
|
159
|
+
if self.src == old_expr:
|
|
160
|
+
r_src = True
|
|
161
|
+
replaced_src = new_expr
|
|
162
|
+
else:
|
|
163
|
+
r_src, replaced_src = self.src.replace(old_expr, new_expr)
|
|
164
|
+
|
|
165
|
+
if r_dst or r_src:
|
|
166
|
+
return True, WeakAssignment(self.idx, replaced_dst, replaced_src, **self.tags)
|
|
167
|
+
return False, self
|
|
168
|
+
|
|
169
|
+
def copy(self) -> WeakAssignment:
|
|
170
|
+
return WeakAssignment(self.idx, self.dst, self.src, **self.tags)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class Store(Statement):
|
|
174
|
+
"""
|
|
175
|
+
Store statement: *addr = data
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
__slots__ = (
|
|
179
|
+
"addr",
|
|
180
|
+
"data",
|
|
181
|
+
"endness",
|
|
182
|
+
"guard",
|
|
183
|
+
"offset",
|
|
184
|
+
"size",
|
|
185
|
+
"variable",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
idx: int | None,
|
|
191
|
+
addr: Expression,
|
|
192
|
+
data: Expression,
|
|
193
|
+
size: int,
|
|
194
|
+
endness: str,
|
|
195
|
+
guard: Expression | None = None,
|
|
196
|
+
variable=None,
|
|
197
|
+
offset=None,
|
|
198
|
+
**kwargs,
|
|
199
|
+
):
|
|
200
|
+
super().__init__(idx, **kwargs)
|
|
201
|
+
|
|
202
|
+
self.addr = addr
|
|
203
|
+
self.data = data
|
|
204
|
+
self.size = size
|
|
205
|
+
self.endness = endness
|
|
206
|
+
self.variable = variable
|
|
207
|
+
self.guard = guard
|
|
208
|
+
self.offset = offset # variable_offset
|
|
209
|
+
|
|
210
|
+
def __eq__(self, other):
|
|
211
|
+
return (
|
|
212
|
+
type(other) is Store
|
|
213
|
+
and self.idx == other.idx
|
|
214
|
+
and self.eq(self.addr, other.addr)
|
|
215
|
+
and self.eq(self.data, other.data)
|
|
216
|
+
and self.size == other.size
|
|
217
|
+
and self.guard == other.guard
|
|
218
|
+
and self.endness == other.endness
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def likes(self, other):
|
|
222
|
+
return (
|
|
223
|
+
type(other) is Store
|
|
224
|
+
and self.addr.likes(other.addr)
|
|
225
|
+
and self.data.likes(other.data)
|
|
226
|
+
and self.size == other.size
|
|
227
|
+
and self.guard == other.guard
|
|
228
|
+
and self.endness == other.endness
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def matches(self, other):
|
|
232
|
+
return (
|
|
233
|
+
type(other) is Store
|
|
234
|
+
and self.addr.matches(other.addr)
|
|
235
|
+
and self.data.matches(other.data)
|
|
236
|
+
and self.size == other.size
|
|
237
|
+
and self.guard == other.guard
|
|
238
|
+
and self.endness == other.endness
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
__hash__ = TaggedObject.__hash__
|
|
242
|
+
|
|
243
|
+
def _hash_core(self):
|
|
244
|
+
return stable_hash((Store, self.idx, self.addr, self.data, self.size, self.endness, self.guard))
|
|
245
|
+
|
|
246
|
+
def __repr__(self):
|
|
247
|
+
return f"Store ({self.addr}, {self.data}[{self.size}])" + ("" if self.guard is None else f"[{self.guard}]")
|
|
248
|
+
|
|
249
|
+
def __str__(self):
|
|
250
|
+
if self.variable is None:
|
|
251
|
+
return f"STORE(addr={self.addr}, data={self.data!s}, size={self.size}, endness={self.endness}, guard={self.guard})"
|
|
252
|
+
return f"{self.variable.name} ={'L' if self.endness == 'Iend_LE' else 'B'} {self.data}<{self.size}>" + (
|
|
253
|
+
"" if self.guard is None else f"[{self.guard}]"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
def replace(self, old_expr, new_expr):
|
|
257
|
+
if self.addr.likes(old_expr):
|
|
258
|
+
r_addr = True
|
|
259
|
+
replaced_addr = new_expr
|
|
260
|
+
else:
|
|
261
|
+
r_addr, replaced_addr = self.addr.replace(old_expr, new_expr)
|
|
262
|
+
|
|
263
|
+
if isinstance(self.data, Expression):
|
|
264
|
+
if self.data.likes(old_expr):
|
|
265
|
+
r_data = True
|
|
266
|
+
replaced_data = new_expr
|
|
267
|
+
else:
|
|
268
|
+
r_data, replaced_data = self.data.replace(old_expr, new_expr)
|
|
269
|
+
else:
|
|
270
|
+
r_data, replaced_data = False, self.data
|
|
271
|
+
|
|
272
|
+
if self.guard is not None:
|
|
273
|
+
r_guard, replaced_guard = self.guard.replace(old_expr, new_expr)
|
|
274
|
+
else:
|
|
275
|
+
r_guard, replaced_guard = False, None
|
|
276
|
+
|
|
277
|
+
if r_addr or r_data or r_guard:
|
|
278
|
+
return True, Store(
|
|
279
|
+
self.idx,
|
|
280
|
+
replaced_addr,
|
|
281
|
+
replaced_data,
|
|
282
|
+
self.size,
|
|
283
|
+
self.endness,
|
|
284
|
+
guard=replaced_guard,
|
|
285
|
+
variable=self.variable,
|
|
286
|
+
**self.tags,
|
|
287
|
+
)
|
|
288
|
+
return False, self
|
|
289
|
+
|
|
290
|
+
def copy(self) -> Store:
|
|
291
|
+
return Store(
|
|
292
|
+
self.idx,
|
|
293
|
+
self.addr,
|
|
294
|
+
self.data,
|
|
295
|
+
self.size,
|
|
296
|
+
self.endness,
|
|
297
|
+
guard=self.guard,
|
|
298
|
+
variable=self.variable,
|
|
299
|
+
offset=self.offset,
|
|
300
|
+
**self.tags,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class Jump(Statement):
|
|
305
|
+
"""
|
|
306
|
+
Jump statement: goto target
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
__slots__ = (
|
|
310
|
+
"target",
|
|
311
|
+
"target_idx",
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def __init__(self, idx: int | None, target: Expression, target_idx: int | None = None, **kwargs):
|
|
315
|
+
super().__init__(idx, **kwargs)
|
|
316
|
+
|
|
317
|
+
self.target = target
|
|
318
|
+
self.target_idx = target_idx
|
|
319
|
+
|
|
320
|
+
def __eq__(self, other):
|
|
321
|
+
return type(other) is Jump and self.idx == other.idx and self.target == other.target
|
|
322
|
+
|
|
323
|
+
def likes(self, other):
|
|
324
|
+
return type(other) is Jump and is_none_or_likeable(self.target, other.target)
|
|
325
|
+
|
|
326
|
+
def matches(self, other):
|
|
327
|
+
return type(other) is Jump and is_none_or_matchable(self.target, other.target)
|
|
328
|
+
|
|
329
|
+
__hash__ = TaggedObject.__hash__
|
|
330
|
+
|
|
331
|
+
def _hash_core(self):
|
|
332
|
+
return stable_hash((Jump, self.idx, self.target))
|
|
333
|
+
|
|
334
|
+
def __repr__(self):
|
|
335
|
+
if self.target_idx is not None:
|
|
336
|
+
return f"Jump ({self.target}.{self.target_idx})"
|
|
337
|
+
return f"Jump ({self.target})"
|
|
338
|
+
|
|
339
|
+
def __str__(self):
|
|
340
|
+
if self.target_idx is not None:
|
|
341
|
+
return f"Goto({self.target}.{self.target_idx})"
|
|
342
|
+
return f"Goto({self.target})"
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def depth(self):
|
|
346
|
+
return self.target.depth
|
|
347
|
+
|
|
348
|
+
def replace(self, old_expr, new_expr):
|
|
349
|
+
r, replaced_target = self.target.replace(old_expr, new_expr)
|
|
350
|
+
|
|
351
|
+
if r:
|
|
352
|
+
return True, Jump(self.idx, replaced_target, **self.tags)
|
|
353
|
+
return False, self
|
|
354
|
+
|
|
355
|
+
def copy(self):
|
|
356
|
+
return Jump(
|
|
357
|
+
self.idx,
|
|
358
|
+
self.target,
|
|
359
|
+
**self.tags,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class ConditionalJump(Statement):
|
|
364
|
+
"""
|
|
365
|
+
if (cond) {true_target} else {false_target}
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
__slots__ = (
|
|
369
|
+
"condition",
|
|
370
|
+
"false_target",
|
|
371
|
+
"false_target_idx",
|
|
372
|
+
"true_target",
|
|
373
|
+
"true_target_idx",
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
def __init__(
|
|
377
|
+
self,
|
|
378
|
+
idx: int | None,
|
|
379
|
+
condition: Expression,
|
|
380
|
+
true_target: Expression | None,
|
|
381
|
+
false_target: Expression | None,
|
|
382
|
+
true_target_idx: int | None = None,
|
|
383
|
+
false_target_idx: int | None = None,
|
|
384
|
+
**kwargs,
|
|
385
|
+
):
|
|
386
|
+
super().__init__(idx, **kwargs)
|
|
387
|
+
|
|
388
|
+
self.condition = condition
|
|
389
|
+
self.true_target = true_target
|
|
390
|
+
self.false_target = false_target
|
|
391
|
+
self.true_target_idx = true_target_idx
|
|
392
|
+
self.false_target_idx = false_target_idx
|
|
393
|
+
|
|
394
|
+
def __eq__(self, other):
|
|
395
|
+
return (
|
|
396
|
+
type(other) is ConditionalJump
|
|
397
|
+
and self.idx == other.idx
|
|
398
|
+
and self.condition == other.condition
|
|
399
|
+
and self.true_target == other.true_target
|
|
400
|
+
and self.false_target == other.false_target
|
|
401
|
+
and self.true_target_idx == other.true_target_idx
|
|
402
|
+
and self.false_target_idx == other.false_target_idx
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
def likes(self, other):
|
|
406
|
+
return (
|
|
407
|
+
type(other) is ConditionalJump
|
|
408
|
+
and self.condition.likes(other.condition)
|
|
409
|
+
and is_none_or_likeable(self.true_target, other.true_target)
|
|
410
|
+
and is_none_or_likeable(self.false_target, other.false_target)
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
def matches(self, other):
|
|
414
|
+
return (
|
|
415
|
+
type(other) is ConditionalJump
|
|
416
|
+
and self.condition.matches(other.condition)
|
|
417
|
+
and is_none_or_matchable(self.true_target, other.true_target)
|
|
418
|
+
and is_none_or_matchable(self.false_target, other.false_target)
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
__hash__ = TaggedObject.__hash__
|
|
422
|
+
|
|
423
|
+
def _hash_core(self):
|
|
424
|
+
return stable_hash(
|
|
425
|
+
(
|
|
426
|
+
ConditionalJump,
|
|
427
|
+
self.idx,
|
|
428
|
+
self.condition,
|
|
429
|
+
self.true_target,
|
|
430
|
+
self.false_target,
|
|
431
|
+
self.true_target_idx,
|
|
432
|
+
self.false_target_idx,
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
def __repr__(self):
|
|
437
|
+
return "ConditionalJump (condition: {}, true: {}{}, false: {}{})".format(
|
|
438
|
+
self.condition,
|
|
439
|
+
self.true_target,
|
|
440
|
+
"" if self.true_target_idx is None else f".{self.true_target_idx}",
|
|
441
|
+
self.false_target,
|
|
442
|
+
"" if self.false_target_idx is None else f".{self.false_target_idx}",
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
def __str__(self):
|
|
446
|
+
return "if ({}) {{ Goto {}{} }} else {{ Goto {}{} }}".format(
|
|
447
|
+
self.condition,
|
|
448
|
+
self.true_target,
|
|
449
|
+
"" if self.true_target_idx is None else f".{self.true_target_idx}",
|
|
450
|
+
self.false_target,
|
|
451
|
+
"" if self.false_target_idx is None else f".{self.false_target_idx}",
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
def replace(self, old_expr, new_expr):
|
|
455
|
+
if self.condition == old_expr:
|
|
456
|
+
r_cond = True
|
|
457
|
+
replaced_cond = new_expr
|
|
458
|
+
else:
|
|
459
|
+
r_cond, replaced_cond = self.condition.replace(old_expr, new_expr)
|
|
460
|
+
|
|
461
|
+
if self.true_target is not None:
|
|
462
|
+
if self.true_target == old_expr:
|
|
463
|
+
r_true = True
|
|
464
|
+
replaced_true = new_expr
|
|
465
|
+
else:
|
|
466
|
+
r_true, replaced_true = self.true_target.replace(old_expr, new_expr)
|
|
467
|
+
else:
|
|
468
|
+
r_true, replaced_true = False, self.true_target
|
|
469
|
+
|
|
470
|
+
if self.false_target is not None:
|
|
471
|
+
if self.false_target == old_expr:
|
|
472
|
+
r_false = True
|
|
473
|
+
replaced_false = new_expr
|
|
474
|
+
else:
|
|
475
|
+
r_false, replaced_false = self.false_target.replace(old_expr, new_expr)
|
|
476
|
+
else:
|
|
477
|
+
r_false, replaced_false = False, self.false_target
|
|
478
|
+
|
|
479
|
+
r = r_cond or r_true or r_false
|
|
480
|
+
|
|
481
|
+
if r:
|
|
482
|
+
return True, ConditionalJump(
|
|
483
|
+
self.idx,
|
|
484
|
+
replaced_cond,
|
|
485
|
+
replaced_true,
|
|
486
|
+
replaced_false,
|
|
487
|
+
true_target_idx=self.true_target_idx,
|
|
488
|
+
false_target_idx=self.false_target_idx,
|
|
489
|
+
**self.tags,
|
|
490
|
+
)
|
|
491
|
+
return False, self
|
|
492
|
+
|
|
493
|
+
def copy(self) -> ConditionalJump:
|
|
494
|
+
return ConditionalJump(
|
|
495
|
+
self.idx,
|
|
496
|
+
self.condition,
|
|
497
|
+
self.true_target,
|
|
498
|
+
self.false_target,
|
|
499
|
+
true_target_idx=self.true_target_idx,
|
|
500
|
+
false_target_idx=self.false_target_idx,
|
|
501
|
+
**self.tags,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class Call(Expression, Statement):
|
|
506
|
+
"""
|
|
507
|
+
Call is both an expression and a statement.
|
|
508
|
+
|
|
509
|
+
When used as a statement, it will set ret_expr, fp_ret_expr, or both if both of them should hold return values.
|
|
510
|
+
When used as an expression, both ret_expr and fp_ret_expr should be None (and should be ignored). The size of the
|
|
511
|
+
call expression is stored in the bits attribute.
|
|
512
|
+
"""
|
|
513
|
+
|
|
514
|
+
__slots__ = (
|
|
515
|
+
"args",
|
|
516
|
+
"calling_convention",
|
|
517
|
+
"fp_ret_expr",
|
|
518
|
+
"prototype",
|
|
519
|
+
"ret_expr",
|
|
520
|
+
"target",
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
def __init__(
|
|
524
|
+
self,
|
|
525
|
+
idx: int | None,
|
|
526
|
+
target: Expression | str,
|
|
527
|
+
calling_convention: SimCC | None = None,
|
|
528
|
+
prototype=None,
|
|
529
|
+
args: Sequence[Expression] | None = None,
|
|
530
|
+
ret_expr: Expression | None = None,
|
|
531
|
+
fp_ret_expr: Expression | None = None,
|
|
532
|
+
bits: int | None = None,
|
|
533
|
+
**kwargs,
|
|
534
|
+
):
|
|
535
|
+
super().__init__(idx, target.depth + 1 if isinstance(target, Expression) else 1, **kwargs)
|
|
536
|
+
|
|
537
|
+
self.target = target
|
|
538
|
+
self.calling_convention = calling_convention
|
|
539
|
+
self.prototype = prototype
|
|
540
|
+
self.args = args
|
|
541
|
+
self.ret_expr = ret_expr
|
|
542
|
+
self.fp_ret_expr = fp_ret_expr
|
|
543
|
+
if bits is not None:
|
|
544
|
+
self.bits = bits
|
|
545
|
+
elif ret_expr is not None:
|
|
546
|
+
self.bits = ret_expr.bits
|
|
547
|
+
elif fp_ret_expr is not None:
|
|
548
|
+
self.bits = fp_ret_expr.bits
|
|
549
|
+
else:
|
|
550
|
+
self.bits = 0 # uhhhhhhhhhhhhhhhhhhh
|
|
551
|
+
|
|
552
|
+
def likes(self, other):
|
|
553
|
+
return (
|
|
554
|
+
type(other) is Call
|
|
555
|
+
and is_none_or_likeable(self.target, other.target)
|
|
556
|
+
and self.calling_convention == other.calling_convention
|
|
557
|
+
and self.prototype == other.prototype
|
|
558
|
+
and is_none_or_likeable(self.args, other.args, is_list=True)
|
|
559
|
+
and is_none_or_likeable(self.ret_expr, other.ret_expr)
|
|
560
|
+
and is_none_or_likeable(self.fp_ret_expr, other.fp_ret_expr)
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
def matches(self, other):
|
|
564
|
+
return (
|
|
565
|
+
type(other) is Call
|
|
566
|
+
and is_none_or_matchable(self.target, other.target)
|
|
567
|
+
and self.calling_convention == other.calling_convention
|
|
568
|
+
and self.prototype == other.prototype
|
|
569
|
+
and is_none_or_matchable(self.args, other.args, is_list=True)
|
|
570
|
+
and is_none_or_matchable(self.ret_expr, other.ret_expr)
|
|
571
|
+
and is_none_or_matchable(self.fp_ret_expr, other.fp_ret_expr)
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
__hash__ = TaggedObject.__hash__ # type: ignore
|
|
575
|
+
|
|
576
|
+
def _hash_core(self):
|
|
577
|
+
return stable_hash((Call, self.idx, self.target))
|
|
578
|
+
|
|
579
|
+
def __repr__(self):
|
|
580
|
+
return f"Call (target: {self.target}, prototype: {self.prototype}, args: {self.args})"
|
|
581
|
+
|
|
582
|
+
def __str__(self):
|
|
583
|
+
cc = "Unknown CC" if self.calling_convention is None else f"{self.calling_convention}"
|
|
584
|
+
if self.args is None:
|
|
585
|
+
if self.calling_convention is not None:
|
|
586
|
+
s = (
|
|
587
|
+
(f"{cc}")
|
|
588
|
+
if self.prototype is None
|
|
589
|
+
else f"{self.calling_convention}: {self.calling_convention.arg_locs(self.prototype)}"
|
|
590
|
+
)
|
|
591
|
+
else:
|
|
592
|
+
s = (f"{cc}") if self.prototype is None else repr(self.prototype)
|
|
593
|
+
else:
|
|
594
|
+
s = (f"{cc}: {self.args}") if self.prototype is None else f"{self.calling_convention}: {self.args}"
|
|
595
|
+
|
|
596
|
+
ret_s = "no-ret-value" if self.ret_expr is None else f"{self.ret_expr}"
|
|
597
|
+
fp_ret_s = "no-fp-ret-value" if self.fp_ret_expr is None else f"{self.fp_ret_expr}"
|
|
598
|
+
|
|
599
|
+
return f"Call({self.target}, {s}, ret: {ret_s}, fp_ret: {fp_ret_s})"
|
|
600
|
+
|
|
601
|
+
@property
|
|
602
|
+
def size(self):
|
|
603
|
+
return self.bits // 8
|
|
604
|
+
|
|
605
|
+
@property
|
|
606
|
+
def verbose_op(self):
|
|
607
|
+
return "call"
|
|
608
|
+
|
|
609
|
+
@property
|
|
610
|
+
def op(self):
|
|
611
|
+
return "call"
|
|
612
|
+
|
|
613
|
+
def replace(self, old_expr: Expression, new_expr: Expression):
|
|
614
|
+
if isinstance(self.target, Expression):
|
|
615
|
+
r0, replaced_target = self.target.replace(old_expr, new_expr)
|
|
616
|
+
else:
|
|
617
|
+
r0 = False
|
|
618
|
+
replaced_target = self.target
|
|
619
|
+
|
|
620
|
+
r = r0
|
|
621
|
+
|
|
622
|
+
new_args = None
|
|
623
|
+
if self.args:
|
|
624
|
+
new_args = []
|
|
625
|
+
for arg in self.args:
|
|
626
|
+
if arg == old_expr:
|
|
627
|
+
r_arg = True
|
|
628
|
+
replaced_arg = new_expr
|
|
629
|
+
else:
|
|
630
|
+
r_arg, replaced_arg = arg.replace(old_expr, new_expr)
|
|
631
|
+
r |= r_arg
|
|
632
|
+
new_args.append(replaced_arg)
|
|
633
|
+
|
|
634
|
+
new_ret_expr = self.ret_expr
|
|
635
|
+
new_bits = self.bits
|
|
636
|
+
if self.ret_expr:
|
|
637
|
+
if self.ret_expr == old_expr:
|
|
638
|
+
r_ret = True
|
|
639
|
+
replaced_ret = new_expr
|
|
640
|
+
else:
|
|
641
|
+
r_ret, replaced_ret = self.ret_expr.replace(old_expr, new_expr)
|
|
642
|
+
r |= r_ret
|
|
643
|
+
new_ret_expr = replaced_ret
|
|
644
|
+
if replaced_ret is not None:
|
|
645
|
+
new_bits = replaced_ret.bits
|
|
646
|
+
|
|
647
|
+
new_fp_ret_expr = self.fp_ret_expr
|
|
648
|
+
if self.fp_ret_expr:
|
|
649
|
+
if self.fp_ret_expr == old_expr:
|
|
650
|
+
r_ret = True
|
|
651
|
+
replaced_fp_ret = new_expr
|
|
652
|
+
else:
|
|
653
|
+
r_ret, replaced_fp_ret = self.fp_ret_expr.replace(old_expr, new_expr)
|
|
654
|
+
r |= r_ret
|
|
655
|
+
new_fp_ret_expr = replaced_fp_ret
|
|
656
|
+
|
|
657
|
+
if r:
|
|
658
|
+
return True, Call(
|
|
659
|
+
self.idx,
|
|
660
|
+
replaced_target,
|
|
661
|
+
calling_convention=self.calling_convention,
|
|
662
|
+
prototype=self.prototype,
|
|
663
|
+
args=new_args,
|
|
664
|
+
ret_expr=new_ret_expr,
|
|
665
|
+
fp_ret_expr=new_fp_ret_expr,
|
|
666
|
+
bits=new_bits,
|
|
667
|
+
**self.tags,
|
|
668
|
+
)
|
|
669
|
+
return False, self
|
|
670
|
+
|
|
671
|
+
def copy(self):
|
|
672
|
+
return Call(
|
|
673
|
+
self.idx,
|
|
674
|
+
self.target,
|
|
675
|
+
calling_convention=self.calling_convention,
|
|
676
|
+
prototype=self.prototype,
|
|
677
|
+
args=self.args[::] if self.args is not None else None,
|
|
678
|
+
ret_expr=self.ret_expr,
|
|
679
|
+
fp_ret_expr=self.fp_ret_expr,
|
|
680
|
+
bits=self.bits,
|
|
681
|
+
**self.tags,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
class Return(Statement):
|
|
686
|
+
"""
|
|
687
|
+
Return statement: (return expr_a), (return)
|
|
688
|
+
"""
|
|
689
|
+
|
|
690
|
+
__slots__ = ("ret_exprs",)
|
|
691
|
+
|
|
692
|
+
def __init__(self, idx: int | None, ret_exprs, **kwargs):
|
|
693
|
+
super().__init__(idx, **kwargs)
|
|
694
|
+
self.ret_exprs = ret_exprs if isinstance(ret_exprs, list) else list(ret_exprs)
|
|
695
|
+
|
|
696
|
+
def __eq__(self, other):
|
|
697
|
+
return type(other) is Return and self.idx == other.idx and self.ret_exprs == other.ret_exprs
|
|
698
|
+
|
|
699
|
+
def likes(self, other):
|
|
700
|
+
return type(other) is Return and is_none_or_likeable(self.ret_exprs, other.ret_exprs, is_list=True)
|
|
701
|
+
|
|
702
|
+
def matches(self, other):
|
|
703
|
+
return type(other) is Return and is_none_or_matchable(self.ret_exprs, other.ret_exprs, is_list=True)
|
|
704
|
+
|
|
705
|
+
__hash__ = TaggedObject.__hash__
|
|
706
|
+
|
|
707
|
+
def _hash_core(self):
|
|
708
|
+
return stable_hash((Return, self.idx, tuple(self.ret_exprs)))
|
|
709
|
+
|
|
710
|
+
def __repr__(self):
|
|
711
|
+
return "Return to ({})".format(",".join(repr(x) for x in self.ret_exprs))
|
|
712
|
+
|
|
713
|
+
def __str__(self):
|
|
714
|
+
exprs = ",".join(str(ret_expr) for ret_expr in self.ret_exprs)
|
|
715
|
+
if not exprs:
|
|
716
|
+
return "return;"
|
|
717
|
+
return f"return {exprs};"
|
|
718
|
+
|
|
719
|
+
def replace(self, old_expr, new_expr):
|
|
720
|
+
new_ret_exprs = []
|
|
721
|
+
replaced = False
|
|
722
|
+
|
|
723
|
+
for expr in self.ret_exprs:
|
|
724
|
+
if expr == old_expr:
|
|
725
|
+
r_expr = True
|
|
726
|
+
replaced_expr = new_expr
|
|
727
|
+
else:
|
|
728
|
+
r_expr, replaced_expr = expr.replace(old_expr, new_expr)
|
|
729
|
+
if r_expr:
|
|
730
|
+
replaced = True
|
|
731
|
+
new_ret_exprs.append(replaced_expr)
|
|
732
|
+
else:
|
|
733
|
+
new_ret_exprs.append(expr)
|
|
734
|
+
|
|
735
|
+
if replaced:
|
|
736
|
+
return True, Return(
|
|
737
|
+
self.idx,
|
|
738
|
+
new_ret_exprs,
|
|
739
|
+
**self.tags,
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
return False, self
|
|
743
|
+
|
|
744
|
+
def copy(self):
|
|
745
|
+
return Return(
|
|
746
|
+
self.idx,
|
|
747
|
+
self.ret_exprs[::],
|
|
748
|
+
**self.tags,
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
class CAS(Statement):
|
|
753
|
+
"""
|
|
754
|
+
Atomic compare-and-swap.
|
|
755
|
+
|
|
756
|
+
*_lo and *_hi are used to represent the low and high parts of a 128-bit CAS operation; *_hi is None if the CAS
|
|
757
|
+
operation works on values that are less than or equal to 64 bits.
|
|
758
|
+
|
|
759
|
+
addr: The address to be compared and swapped.
|
|
760
|
+
data: The value to be written if the comparison is successful.
|
|
761
|
+
expd: The expected value to be compared against.
|
|
762
|
+
old: The value that is currently stored at addr before compare-and-swap; it will be returned after compare-and-swap.
|
|
763
|
+
"""
|
|
764
|
+
|
|
765
|
+
__slots__ = ("addr", "data_hi", "data_lo", "endness", "expd_hi", "expd_lo", "old_hi", "old_lo")
|
|
766
|
+
|
|
767
|
+
def __init__(
|
|
768
|
+
self,
|
|
769
|
+
idx: int | None,
|
|
770
|
+
addr: Expression,
|
|
771
|
+
data_lo: Expression,
|
|
772
|
+
data_hi: Expression | None,
|
|
773
|
+
expd_lo: Expression,
|
|
774
|
+
expd_hi: Expression | None,
|
|
775
|
+
old_lo: Atom,
|
|
776
|
+
old_hi: Atom | None,
|
|
777
|
+
endness: str,
|
|
778
|
+
**kwargs,
|
|
779
|
+
):
|
|
780
|
+
super().__init__(idx, **kwargs)
|
|
781
|
+
self.addr = addr
|
|
782
|
+
self.data_lo = data_lo
|
|
783
|
+
self.data_hi = data_hi
|
|
784
|
+
self.expd_lo = expd_lo
|
|
785
|
+
self.expd_hi = expd_hi
|
|
786
|
+
self.old_lo = old_lo
|
|
787
|
+
self.old_hi = old_hi
|
|
788
|
+
self.endness = endness
|
|
789
|
+
|
|
790
|
+
def _hash_core(self):
|
|
791
|
+
return stable_hash(
|
|
792
|
+
(
|
|
793
|
+
CAS,
|
|
794
|
+
self.idx,
|
|
795
|
+
self.addr,
|
|
796
|
+
self.data_lo,
|
|
797
|
+
self.data_hi,
|
|
798
|
+
self.expd_lo,
|
|
799
|
+
self.expd_hi,
|
|
800
|
+
self.old_lo,
|
|
801
|
+
self.old_hi,
|
|
802
|
+
self.endness,
|
|
803
|
+
)
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
__hash__ = TaggedObject.__hash__
|
|
807
|
+
|
|
808
|
+
def __repr__(self):
|
|
809
|
+
if self.old_hi is None:
|
|
810
|
+
return f"CAS({self.addr}, {self.data_lo}, {self.expd_lo}, {self.old_lo})"
|
|
811
|
+
return (
|
|
812
|
+
f"CAS({self.addr}, {self.data_hi} .. {self.data_lo}, {self.expd_hi} .. {self.expd_lo}, "
|
|
813
|
+
f"{self.old_hi} .. {self.old_lo})"
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
def __str__(self):
|
|
817
|
+
if self.old_hi is None:
|
|
818
|
+
return f"{self.old_lo} = CAS({self.addr}, {self.data_lo}, {self.expd_lo})"
|
|
819
|
+
return (
|
|
820
|
+
f"{self.old_hi} .. {self.old_lo} = CAS({self.addr}, {self.data_hi} .. {self.data_lo}, "
|
|
821
|
+
f"{self.expd_hi} .. {self.expd_lo})"
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
def replace(self, old_expr: Expression, new_expr: Expression) -> tuple[bool, CAS]:
|
|
825
|
+
r_addr, replaced_addr = self.addr.replace(old_expr, new_expr)
|
|
826
|
+
r_data_lo, replaced_data_lo = self.data_lo.replace(old_expr, new_expr)
|
|
827
|
+
r_data_hi, replaced_data_hi = self.data_hi.replace(old_expr, new_expr) if self.data_hi else (False, None)
|
|
828
|
+
r_expd_lo, replaced_expd_lo = self.expd_lo.replace(old_expr, new_expr)
|
|
829
|
+
r_expd_hi, replaced_expd_hi = self.expd_hi.replace(old_expr, new_expr) if self.expd_hi else (False, None)
|
|
830
|
+
r_old_lo, replaced_old_lo = self.old_lo.replace(old_expr, new_expr)
|
|
831
|
+
r_old_hi, replaced_old_hi = self.old_hi.replace(old_expr, new_expr) if self.old_hi else (False, None)
|
|
832
|
+
|
|
833
|
+
if r_addr or r_data_lo or r_data_hi or r_expd_lo or r_expd_hi or r_old_lo or r_old_hi:
|
|
834
|
+
return True, CAS(
|
|
835
|
+
self.idx,
|
|
836
|
+
replaced_addr,
|
|
837
|
+
replaced_data_lo,
|
|
838
|
+
replaced_data_hi,
|
|
839
|
+
replaced_expd_lo,
|
|
840
|
+
replaced_expd_hi,
|
|
841
|
+
replaced_old_lo,
|
|
842
|
+
replaced_old_hi,
|
|
843
|
+
endness=self.endness,
|
|
844
|
+
**self.tags,
|
|
845
|
+
)
|
|
846
|
+
return False, self
|
|
847
|
+
|
|
848
|
+
def copy(self) -> CAS:
|
|
849
|
+
return CAS(
|
|
850
|
+
self.idx,
|
|
851
|
+
self.addr,
|
|
852
|
+
self.data_lo,
|
|
853
|
+
self.data_hi,
|
|
854
|
+
self.expd_lo,
|
|
855
|
+
self.expd_hi,
|
|
856
|
+
self.old_lo,
|
|
857
|
+
self.old_hi,
|
|
858
|
+
endness=self.endness,
|
|
859
|
+
**self.tags,
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
def likes(self, other) -> bool:
|
|
863
|
+
return (
|
|
864
|
+
type(other) is CAS
|
|
865
|
+
and self.addr.likes(other.addr)
|
|
866
|
+
and self.data_lo.likes(other.data_lo)
|
|
867
|
+
and (self.data_hi is None or self.data_hi.likes(other.data_hi))
|
|
868
|
+
and self.expd_lo.likes(other.expd_lo)
|
|
869
|
+
and (self.expd_hi is None or self.expd_hi.likes(other.expd_hi))
|
|
870
|
+
and self.old_lo.likes(other.old_lo)
|
|
871
|
+
and (self.old_hi is None or self.old_hi.likes(other.old_hi))
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
def matches(self, other) -> bool:
|
|
875
|
+
return (
|
|
876
|
+
type(other) is CAS
|
|
877
|
+
and self.addr.matches(other.addr)
|
|
878
|
+
and self.data_lo.matches(other.data_lo)
|
|
879
|
+
and (self.data_hi is None or self.data_hi.matches(other.data_hi))
|
|
880
|
+
and self.expd_lo.matches(other.expd_lo)
|
|
881
|
+
and (self.expd_hi is None or self.expd_hi.matches(other.expd_hi))
|
|
882
|
+
and self.old_lo.matches(other.old_lo)
|
|
883
|
+
and (self.old_hi is None or self.old_hi.matches(other.old_hi))
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
@property
|
|
887
|
+
def bits(self) -> int:
|
|
888
|
+
if self.old_hi is None:
|
|
889
|
+
return self.old_lo.bits
|
|
890
|
+
return self.old_lo.bits + self.old_hi.bits
|
|
891
|
+
|
|
892
|
+
@property
|
|
893
|
+
def size(self) -> int:
|
|
894
|
+
return self.bits // 8
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
class DirtyStatement(Statement):
|
|
898
|
+
"""
|
|
899
|
+
Wrapper around the original statement, which is usually not convertible (temporarily).
|
|
900
|
+
"""
|
|
901
|
+
|
|
902
|
+
__slots__ = ("dirty",)
|
|
903
|
+
|
|
904
|
+
def __init__(self, idx: int | None, dirty: DirtyExpression, **kwargs):
|
|
905
|
+
super().__init__(idx, **kwargs)
|
|
906
|
+
self.dirty = dirty
|
|
907
|
+
|
|
908
|
+
def _hash_core(self):
|
|
909
|
+
return stable_hash((DirtyStatement, self.dirty))
|
|
910
|
+
|
|
911
|
+
def __repr__(self):
|
|
912
|
+
return repr(self.dirty)
|
|
913
|
+
|
|
914
|
+
def __str__(self):
|
|
915
|
+
return str(self.dirty)
|
|
916
|
+
|
|
917
|
+
def replace(self, old_expr, new_expr):
|
|
918
|
+
if self.dirty == old_expr:
|
|
919
|
+
assert isinstance(new_expr, DirtyExpression)
|
|
920
|
+
return True, DirtyStatement(self.idx, new_expr, **self.tags)
|
|
921
|
+
r, new_dirty = self.dirty.replace(old_expr, new_expr)
|
|
922
|
+
if r:
|
|
923
|
+
return True, DirtyStatement(self.idx, new_dirty, **self.tags)
|
|
924
|
+
return False, self
|
|
925
|
+
|
|
926
|
+
def copy(self) -> DirtyStatement:
|
|
927
|
+
return DirtyStatement(self.idx, self.dirty, **self.tags)
|
|
928
|
+
|
|
929
|
+
def likes(self, other):
|
|
930
|
+
return type(other) is DirtyStatement and self.dirty.likes(other.dirty)
|
|
931
|
+
|
|
932
|
+
def matches(self, other):
|
|
933
|
+
return type(other) is DirtyStatement and self.dirty.matches(other.dirty)
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
class Label(Statement):
|
|
937
|
+
"""
|
|
938
|
+
A dummy statement that indicates a label with a name.
|
|
939
|
+
"""
|
|
940
|
+
|
|
941
|
+
__slots__ = (
|
|
942
|
+
"block_idx",
|
|
943
|
+
"ins_addr",
|
|
944
|
+
"name",
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
def __init__(self, idx: int | None, name: str, ins_addr: int, block_idx: int | None = None, **kwargs):
|
|
948
|
+
super().__init__(idx, **kwargs)
|
|
949
|
+
self.name = name
|
|
950
|
+
self.ins_addr = ins_addr
|
|
951
|
+
self.block_idx = block_idx
|
|
952
|
+
|
|
953
|
+
def likes(self, other: Label):
|
|
954
|
+
return isinstance(other, Label)
|
|
955
|
+
|
|
956
|
+
def replace(self, old_expr, new_expr):
|
|
957
|
+
return False, self
|
|
958
|
+
|
|
959
|
+
matches = likes
|
|
960
|
+
|
|
961
|
+
def _hash_core(self):
|
|
962
|
+
return stable_hash(
|
|
963
|
+
(
|
|
964
|
+
Label,
|
|
965
|
+
self.name,
|
|
966
|
+
self.ins_addr,
|
|
967
|
+
self.block_idx,
|
|
968
|
+
)
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
def __repr__(self):
|
|
972
|
+
return f"Label {self.name}"
|
|
973
|
+
|
|
974
|
+
def __str__(self):
|
|
975
|
+
return f"{self.name}:"
|
|
976
|
+
|
|
977
|
+
def copy(self) -> Label:
|
|
978
|
+
return Label(self.idx, self.name, self.ins_addr, self.block_idx, **self.tags)
|