angr 9.2.130__py3-none-manylinux2014_aarch64.whl → 9.2.132__py3-none-manylinux2014_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/analysis.py +6 -2
- angr/analyses/cfg/cfg_emulated.py +5 -5
- angr/analyses/cfg/cfg_fast.py +2 -2
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +139 -94
- angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +1 -1
- angr/analyses/ddg.py +14 -11
- angr/analyses/decompiler/ail_simplifier.py +3 -2
- angr/analyses/decompiler/block_simplifier.py +10 -21
- angr/analyses/decompiler/clinic.py +361 -8
- angr/analyses/decompiler/condition_processor.py +12 -10
- angr/analyses/decompiler/dephication/graph_rewriting.py +1 -1
- angr/analyses/decompiler/dephication/rewriting_engine.py +169 -45
- angr/analyses/decompiler/dephication/seqnode_dephication.py +5 -4
- angr/analyses/decompiler/optimization_passes/__init__.py +0 -3
- angr/analyses/decompiler/optimization_passes/const_derefs.py +1 -0
- angr/analyses/decompiler/optimization_passes/div_simplifier.py +41 -16
- angr/analyses/decompiler/optimization_passes/engine_base.py +261 -83
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +173 -35
- angr/analyses/decompiler/optimization_passes/mod_simplifier.py +5 -2
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +39 -19
- angr/analyses/decompiler/peephole_optimizations/__init__.py +5 -1
- angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
- angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +3 -1
- angr/analyses/decompiler/peephole_optimizations/bswap.py +10 -6
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +100 -19
- angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +17 -0
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +42 -3
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +4 -2
- angr/analyses/decompiler/peephole_optimizations/rol_ror.py +37 -10
- angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
- angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
- angr/analyses/decompiler/presets/fast.py +0 -2
- angr/analyses/decompiler/presets/full.py +0 -2
- angr/analyses/decompiler/ssailification/rewriting.py +1 -2
- angr/analyses/decompiler/ssailification/rewriting_engine.py +140 -57
- angr/analyses/decompiler/ssailification/ssailification.py +2 -1
- angr/analyses/decompiler/ssailification/traversal.py +4 -6
- angr/analyses/decompiler/ssailification/traversal_engine.py +125 -42
- angr/analyses/decompiler/structured_codegen/c.py +79 -16
- angr/analyses/decompiler/structuring/phoenix.py +40 -14
- angr/analyses/decompiler/structuring/structurer_nodes.py +9 -0
- angr/analyses/deobfuscator/irsb_reg_collector.py +29 -60
- angr/analyses/deobfuscator/string_obf_finder.py +2 -2
- angr/analyses/init_finder.py +47 -22
- angr/analyses/propagator/engine_base.py +21 -14
- angr/analyses/propagator/engine_vex.py +149 -179
- angr/analyses/propagator/propagator.py +10 -28
- angr/analyses/propagator/top_checker_mixin.py +211 -5
- angr/analyses/propagator/vex_vars.py +1 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -1
- angr/analyses/reaching_definitions/engine_ail.py +304 -329
- angr/analyses/reaching_definitions/engine_vex.py +243 -229
- angr/analyses/reaching_definitions/function_handler.py +3 -3
- angr/analyses/reaching_definitions/rd_state.py +37 -32
- angr/analyses/s_propagator.py +38 -5
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +9 -5
- angr/analyses/typehoon/simple_solver.py +16 -7
- angr/analyses/typehoon/translator.py +8 -0
- angr/analyses/typehoon/typeconsts.py +10 -2
- angr/analyses/typehoon/typehoon.py +4 -1
- angr/analyses/typehoon/typevars.py +9 -7
- angr/analyses/variable_recovery/engine_ail.py +296 -256
- angr/analyses/variable_recovery/engine_base.py +137 -116
- angr/analyses/variable_recovery/engine_vex.py +175 -185
- angr/analyses/variable_recovery/irsb_scanner.py +49 -38
- angr/analyses/variable_recovery/variable_recovery.py +28 -5
- angr/analyses/variable_recovery/variable_recovery_base.py +32 -33
- angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
- angr/analyses/xrefs.py +46 -19
- angr/annocfg.py +19 -14
- angr/block.py +4 -9
- angr/calling_conventions.py +1 -1
- angr/engines/engine.py +30 -14
- angr/engines/light/__init__.py +11 -3
- angr/engines/light/engine.py +1003 -1185
- angr/engines/pcode/cc.py +2 -0
- angr/engines/successors.py +13 -9
- angr/engines/vex/claripy/datalayer.py +1 -1
- angr/engines/vex/claripy/irop.py +14 -3
- angr/engines/vex/light/slicing.py +2 -2
- angr/exploration_techniques/__init__.py +1 -124
- angr/exploration_techniques/base.py +126 -0
- angr/exploration_techniques/bucketizer.py +1 -1
- angr/exploration_techniques/dfs.py +3 -1
- angr/exploration_techniques/director.py +2 -3
- angr/exploration_techniques/driller_core.py +1 -1
- angr/exploration_techniques/explorer.py +4 -2
- angr/exploration_techniques/lengthlimiter.py +2 -1
- angr/exploration_techniques/local_loop_seer.py +2 -1
- angr/exploration_techniques/loop_seer.py +5 -5
- angr/exploration_techniques/manual_mergepoint.py +2 -1
- angr/exploration_techniques/memory_watcher.py +3 -1
- angr/exploration_techniques/oppologist.py +4 -5
- angr/exploration_techniques/slicecutor.py +4 -2
- angr/exploration_techniques/spiller.py +1 -1
- angr/exploration_techniques/stochastic.py +2 -1
- angr/exploration_techniques/stub_stasher.py +2 -1
- angr/exploration_techniques/suggestions.py +3 -1
- angr/exploration_techniques/symbion.py +3 -1
- angr/exploration_techniques/tech_builder.py +2 -1
- angr/exploration_techniques/threading.py +4 -7
- angr/exploration_techniques/timeout.py +4 -2
- angr/exploration_techniques/tracer.py +4 -3
- angr/exploration_techniques/unique.py +3 -2
- angr/exploration_techniques/veritesting.py +1 -1
- angr/knowledge_plugins/key_definitions/atoms.py +2 -2
- angr/knowledge_plugins/key_definitions/live_definitions.py +16 -13
- angr/knowledge_plugins/propagations/states.py +13 -8
- angr/knowledge_plugins/variables/variable_manager.py +23 -9
- angr/sim_manager.py +1 -3
- angr/sim_state.py +39 -41
- angr/sim_type.py +5 -0
- angr/sim_variable.py +29 -28
- angr/utils/bits.py +17 -0
- angr/utils/formatting.py +4 -1
- angr/utils/orderedset.py +4 -1
- angr/utils/ssa/__init__.py +21 -3
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/METADATA +6 -6
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/RECORD +124 -123
- angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -223
- angr/analyses/propagator/engine_ail.py +0 -1562
- angr/storage/memory_mixins/__init__.pyi +0 -48
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/LICENSE +0 -0
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/WHEEL +0 -0
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/entry_points.txt +0 -0
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
# pylint:disable=arguments-renamed,too-many-boolean-expressions,no-self-use
|
|
1
|
+
# pylint:disable=arguments-renamed,too-many-boolean-expressions,no-self-use,unused-argument
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from typing import Any
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from collections import defaultdict
|
|
5
6
|
|
|
6
7
|
from archinfo import Endness
|
|
7
8
|
from ailment.expression import (
|
|
8
9
|
Const,
|
|
9
10
|
Register,
|
|
10
|
-
|
|
11
|
+
Expression,
|
|
11
12
|
StackBaseOffset,
|
|
12
13
|
Convert,
|
|
13
14
|
BinaryOp,
|
|
@@ -19,7 +20,8 @@ from ailment.expression import (
|
|
|
19
20
|
from ailment.statement import ConditionalJump, Jump, Assignment
|
|
20
21
|
import claripy
|
|
21
22
|
|
|
22
|
-
from angr.
|
|
23
|
+
from angr.utils.bits import zeroextend_on_demand
|
|
24
|
+
from angr.engines.light import SimEngineNostmtAIL
|
|
23
25
|
from angr.storage.memory_mixins import (
|
|
24
26
|
SimpleInterfaceMixin,
|
|
25
27
|
DefaultFillerMixin,
|
|
@@ -31,6 +33,19 @@ from angr.errors import SimMemoryMissingError
|
|
|
31
33
|
from .optimization_pass import OptimizationPass, OptimizationPassStage
|
|
32
34
|
|
|
33
35
|
|
|
36
|
+
def _make_binop(compute: Callable[[claripy.ast.BV, claripy.ast.BV], claripy.ast.BV]):
|
|
37
|
+
def inner(self, expr: BinaryOp) -> claripy.ast.BV | None:
|
|
38
|
+
a, b = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
39
|
+
if a is None or b is None:
|
|
40
|
+
return None
|
|
41
|
+
try:
|
|
42
|
+
return compute(a, b)
|
|
43
|
+
except (ZeroDivisionError, claripy.ClaripyZeroDivisionError):
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
return inner
|
|
47
|
+
|
|
48
|
+
|
|
34
49
|
class FasterMemory(
|
|
35
50
|
SimpleInterfaceMixin,
|
|
36
51
|
DefaultFillerMixin,
|
|
@@ -61,12 +76,12 @@ class InlinedStringTransformationState:
|
|
|
61
76
|
def _get_weakref(self):
|
|
62
77
|
return self
|
|
63
78
|
|
|
64
|
-
def reg_store(self, reg: Register, value: claripy.ast.
|
|
79
|
+
def reg_store(self, reg: Register, value: claripy.ast.BV) -> None:
|
|
65
80
|
self.registers.store(
|
|
66
81
|
reg.reg_offset, value, size=value.size() // self.arch.byte_width, endness=str(self.arch.register_endness)
|
|
67
82
|
)
|
|
68
83
|
|
|
69
|
-
def reg_load(self, reg: Register) -> claripy.ast.
|
|
84
|
+
def reg_load(self, reg: Register) -> claripy.ast.BV | None:
|
|
70
85
|
try:
|
|
71
86
|
return self.registers.load(
|
|
72
87
|
reg.reg_offset, size=reg.size, endness=self.arch.register_endness, fill_missing=False
|
|
@@ -77,7 +92,7 @@ class InlinedStringTransformationState:
|
|
|
77
92
|
def mem_store(self, addr: int, value: claripy.ast.Bits, endness: str) -> None:
|
|
78
93
|
self.memory.store(addr, value, size=value.size() // self.arch.byte_width, endness=endness)
|
|
79
94
|
|
|
80
|
-
def mem_load(self, addr: int, size: int, endness) -> claripy.ast.
|
|
95
|
+
def mem_load(self, addr: int, size: int, endness) -> claripy.ast.BV | None:
|
|
81
96
|
try:
|
|
82
97
|
return self.memory.load(addr, size=size, endness=str(endness), fill_missing=False)
|
|
83
98
|
except SimMemoryMissingError:
|
|
@@ -86,21 +101,21 @@ class InlinedStringTransformationState:
|
|
|
86
101
|
def vvar_store(self, vvar: VirtualVariable, value: claripy.ast.Bits | None) -> None:
|
|
87
102
|
self.virtual_variables[vvar.varid] = value
|
|
88
103
|
|
|
89
|
-
def vvar_load(self, vvar: VirtualVariable) -> claripy.ast.
|
|
104
|
+
def vvar_load(self, vvar: VirtualVariable) -> claripy.ast.BV | None:
|
|
90
105
|
if vvar.varid in self.virtual_variables:
|
|
91
106
|
return self.virtual_variables[vvar.varid]
|
|
92
107
|
return None
|
|
93
108
|
|
|
94
109
|
|
|
95
|
-
class InlinedStringTransformationAILEngine(
|
|
110
|
+
class InlinedStringTransformationAILEngine(
|
|
111
|
+
SimEngineNostmtAIL[InlinedStringTransformationState, claripy.ast.BV | None, None, None],
|
|
112
|
+
):
|
|
96
113
|
"""
|
|
97
114
|
A simple AIL execution engine
|
|
98
115
|
"""
|
|
99
116
|
|
|
100
117
|
def __init__(self, project, nodes: dict[int, Any], start: int, end: int, step_limit: int):
|
|
101
|
-
super().__init__()
|
|
102
|
-
|
|
103
|
-
self.arch = project.arch
|
|
118
|
+
super().__init__(project)
|
|
104
119
|
self.nodes: dict[int, Any] = nodes
|
|
105
120
|
self.start: int = start
|
|
106
121
|
self.end: int = end
|
|
@@ -121,7 +136,7 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
121
136
|
# jumped to a node that we do not know about
|
|
122
137
|
break
|
|
123
138
|
block = self.nodes[self.pc]
|
|
124
|
-
self._process(state,
|
|
139
|
+
self._process(state, block=block, whitelist=None)
|
|
125
140
|
if self.pc is None:
|
|
126
141
|
# not sure where to jump...
|
|
127
142
|
break
|
|
@@ -131,8 +146,18 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
131
146
|
break
|
|
132
147
|
i += 1
|
|
133
148
|
|
|
134
|
-
def
|
|
149
|
+
def _top(self, bits):
|
|
150
|
+
assert False, "Should not be reachable"
|
|
151
|
+
|
|
152
|
+
def _is_top(self, expr):
|
|
153
|
+
assert False, "Should not be reachable"
|
|
154
|
+
|
|
155
|
+
def _process_block_end(self, block, stmt_data, whitelist):
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
def _process_address(self, addr: Expression) -> tuple[int, str] | None:
|
|
135
159
|
if isinstance(addr, Const):
|
|
160
|
+
assert isinstance(addr.value, int)
|
|
136
161
|
return addr.value, "mem"
|
|
137
162
|
if isinstance(addr, StackBaseOffset):
|
|
138
163
|
return (addr.offset + self.STACK_BASE) & self.MASK, "stack"
|
|
@@ -145,7 +170,7 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
145
170
|
return (v0 + v1.concrete_value) & self.MASK, "stack"
|
|
146
171
|
return None
|
|
147
172
|
|
|
148
|
-
def
|
|
173
|
+
def _handle_stmt_Assignment(self, stmt):
|
|
149
174
|
if isinstance(stmt.dst, VirtualVariable):
|
|
150
175
|
if stmt.dst.was_reg:
|
|
151
176
|
val = self._expr(stmt.src)
|
|
@@ -163,7 +188,7 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
163
188
|
byte_off = val.size() // self.arch.byte_width - i - 1
|
|
164
189
|
self.stack_accesses[addr + i].append(("store", self._codeloc(), val.get_byte(byte_off)))
|
|
165
190
|
|
|
166
|
-
def
|
|
191
|
+
def _handle_stmt_Store(self, stmt):
|
|
167
192
|
addr_and_type = self._process_address(stmt.addr)
|
|
168
193
|
if addr_and_type is not None:
|
|
169
194
|
addr, addr_type = addr_and_type
|
|
@@ -178,14 +203,14 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
178
203
|
byte_off = val.size() // self.arch.byte_width - i - 1
|
|
179
204
|
self.stack_accesses[addr + i].append(("store", self._codeloc(), val.get_byte(byte_off)))
|
|
180
205
|
|
|
181
|
-
def
|
|
206
|
+
def _handle_stmt_Jump(self, stmt):
|
|
182
207
|
self.last_pc = self.pc
|
|
183
208
|
if isinstance(stmt.target, Const):
|
|
184
209
|
self.pc = stmt.target.value
|
|
185
210
|
else:
|
|
186
211
|
self.pc = None
|
|
187
212
|
|
|
188
|
-
def
|
|
213
|
+
def _handle_stmt_ConditionalJump(self, stmt):
|
|
189
214
|
self.last_pc = self.pc
|
|
190
215
|
self.pc = None
|
|
191
216
|
if isinstance(stmt.true_target, Const) and isinstance(stmt.false_target, Const):
|
|
@@ -196,10 +221,12 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
196
221
|
elif isinstance(cond, claripy.ast.Bits) and cond.concrete_value == 0:
|
|
197
222
|
self.pc = stmt.false_target.value
|
|
198
223
|
|
|
199
|
-
def
|
|
200
|
-
|
|
224
|
+
def _handle_expr_Const(self, expr):
|
|
225
|
+
if isinstance(expr.value, int):
|
|
226
|
+
return claripy.BVV(expr.value, expr.bits)
|
|
227
|
+
return None
|
|
201
228
|
|
|
202
|
-
def
|
|
229
|
+
def _handle_expr_Load(self, expr):
|
|
203
230
|
addr_and_type = self._process_address(expr.addr)
|
|
204
231
|
if addr_and_type is not None:
|
|
205
232
|
addr, addr_type = addr_and_type
|
|
@@ -214,14 +241,14 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
214
241
|
return v
|
|
215
242
|
return None
|
|
216
243
|
|
|
217
|
-
def
|
|
244
|
+
def _handle_expr_Register(self, expr: Register):
|
|
218
245
|
return self.state.reg_load(expr)
|
|
219
246
|
|
|
220
|
-
def
|
|
247
|
+
def _handle_expr_VirtualVariable(self, expr: VirtualVariable):
|
|
221
248
|
if expr.was_stack:
|
|
222
249
|
addr = (expr.stack_offset + self.STACK_BASE) & self.MASK
|
|
223
250
|
v = self.state.mem_load(addr, expr.size, self.arch.memory_endness)
|
|
224
|
-
if
|
|
251
|
+
if v is not None:
|
|
225
252
|
# log it
|
|
226
253
|
for i in range(expr.size):
|
|
227
254
|
byte_off = i
|
|
@@ -233,25 +260,43 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
233
260
|
return self.state.vvar_load(expr)
|
|
234
261
|
return None
|
|
235
262
|
|
|
236
|
-
def
|
|
263
|
+
def _handle_expr_Phi(self, expr: Phi):
|
|
237
264
|
for src, vvar in expr.src_and_vvars:
|
|
238
265
|
if src[0] == self.last_pc and vvar is not None:
|
|
239
266
|
return self.state.vvar_load(vvar)
|
|
240
267
|
return None
|
|
241
268
|
|
|
242
|
-
def
|
|
269
|
+
def _handle_unop_Neg(self, expr: UnaryOp):
|
|
243
270
|
v = self._expr(expr.operand)
|
|
244
271
|
if isinstance(v, claripy.ast.Bits):
|
|
245
272
|
return -v
|
|
246
273
|
return None
|
|
247
274
|
|
|
248
|
-
def
|
|
275
|
+
def _handle_unop_Not(self, expr: UnaryOp):
|
|
249
276
|
v = self._expr(expr.operand)
|
|
250
277
|
if isinstance(v, claripy.ast.Bits):
|
|
251
278
|
return ~v
|
|
252
279
|
return None
|
|
253
280
|
|
|
254
|
-
def
|
|
281
|
+
def _handle_unop_BitwiseNeg(self, expr: UnaryOp):
|
|
282
|
+
v = self._expr(expr.operand)
|
|
283
|
+
if isinstance(v, claripy.ast.Bits):
|
|
284
|
+
return ~v
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
def _handle_unop_Default(self, expr: UnaryOp):
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
_handle_unop_Clz = _handle_unop_Default
|
|
291
|
+
_handle_unop_Ctz = _handle_unop_Default
|
|
292
|
+
_handle_unop_Dereference = _handle_unop_Default
|
|
293
|
+
_handle_unop_Reference = _handle_unop_Default
|
|
294
|
+
_handle_unop_GetMSBs = _handle_unop_Default
|
|
295
|
+
_handle_unop_unpack = _handle_unop_Default
|
|
296
|
+
_handle_unop_Sqrt = _handle_unop_Default
|
|
297
|
+
_handle_unop_RSqrtEst = _handle_unop_Default
|
|
298
|
+
|
|
299
|
+
def _handle_expr_Convert(self, expr: Convert):
|
|
255
300
|
v = self._expr(expr.operand)
|
|
256
301
|
if isinstance(v, claripy.ast.Bits):
|
|
257
302
|
if expr.to_bits > expr.from_bits:
|
|
@@ -263,48 +308,139 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
|
|
|
263
308
|
return v
|
|
264
309
|
return None
|
|
265
310
|
|
|
266
|
-
def
|
|
311
|
+
def _handle_binop_CmpEQ(self, expr):
|
|
267
312
|
op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
268
313
|
if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
|
|
269
314
|
return claripy.BVV(1, 1) if op0.concrete_value == op1.concrete_value else claripy.BVV(0, 1)
|
|
270
315
|
return None
|
|
271
316
|
|
|
272
|
-
def
|
|
317
|
+
def _handle_binop_CmpNE(self, expr):
|
|
273
318
|
op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
274
319
|
if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
|
|
275
320
|
return claripy.BVV(1, 1) if op0.concrete_value != op1.concrete_value else claripy.BVV(0, 1)
|
|
276
321
|
return None
|
|
277
322
|
|
|
278
|
-
def
|
|
323
|
+
def _handle_binop_CmpLT(self, expr):
|
|
279
324
|
op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
280
325
|
if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
|
|
281
326
|
return claripy.BVV(1, 1) if op0.concrete_value < op1.concrete_value else claripy.BVV(0, 1)
|
|
282
327
|
return None
|
|
283
328
|
|
|
284
|
-
def
|
|
329
|
+
def _handle_binop_CmpLE(self, expr):
|
|
285
330
|
op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
286
331
|
if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
|
|
287
332
|
return claripy.BVV(1, 1) if op0.concrete_value <= op1.concrete_value else claripy.BVV(0, 1)
|
|
288
333
|
return None
|
|
289
334
|
|
|
290
|
-
def
|
|
335
|
+
def _handle_binop_CmpGT(self, expr):
|
|
291
336
|
op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
292
337
|
if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
|
|
293
338
|
return claripy.BVV(1, 1) if op0.concrete_value > op1.concrete_value else claripy.BVV(0, 1)
|
|
294
339
|
return None
|
|
295
340
|
|
|
296
|
-
def
|
|
341
|
+
def _handle_binop_CmpGE(self, expr):
|
|
297
342
|
op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
298
343
|
if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
|
|
299
344
|
return claripy.BVV(1, 1) if op0.concrete_value >= op1.concrete_value else claripy.BVV(0, 1)
|
|
300
345
|
return None
|
|
301
346
|
|
|
302
|
-
def
|
|
347
|
+
def _handle_stmt_Call(self, stmt):
|
|
303
348
|
pass
|
|
304
349
|
|
|
305
|
-
def
|
|
350
|
+
def _handle_expr_Call(self, expr):
|
|
306
351
|
pass
|
|
307
352
|
|
|
353
|
+
def _handle_expr_BasePointerOffset(self, expr):
|
|
354
|
+
return None
|
|
355
|
+
|
|
356
|
+
def _handle_expr_DirtyExpression(self, expr):
|
|
357
|
+
return None
|
|
358
|
+
|
|
359
|
+
def _handle_expr_ITE(self, expr):
|
|
360
|
+
return None
|
|
361
|
+
|
|
362
|
+
def _handle_expr_MultiStatementExpression(self, expr):
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
def _handle_expr_Reinterpret(self, expr):
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
def _handle_expr_StackBaseOffset(self, expr):
|
|
369
|
+
return None
|
|
370
|
+
|
|
371
|
+
def _handle_expr_Tmp(self, expr):
|
|
372
|
+
try:
|
|
373
|
+
return self.tmps[expr.tmp_idx]
|
|
374
|
+
except KeyError:
|
|
375
|
+
return None
|
|
376
|
+
|
|
377
|
+
def _handle_expr_VEXCCallExpression(self, expr):
|
|
378
|
+
return None
|
|
379
|
+
|
|
380
|
+
def _handle_binop_Default(self, expr):
|
|
381
|
+
self._expr(expr.operands[0])
|
|
382
|
+
self._expr(expr.operands[1])
|
|
383
|
+
|
|
384
|
+
_handle_binop_Add = _make_binop(lambda a, b: a + b)
|
|
385
|
+
_handle_binop_And = _make_binop(lambda a, b: a & b)
|
|
386
|
+
_handle_binop_Concat = _make_binop(lambda a, b: a.concat(b))
|
|
387
|
+
_handle_binop_Div = _make_binop(lambda a, b: a // b)
|
|
388
|
+
_handle_binop_LogicalAnd = _make_binop(lambda a, b: a & b)
|
|
389
|
+
_handle_binop_LogicalOr = _make_binop(lambda a, b: a | b)
|
|
390
|
+
_handle_binop_Mod = _make_binop(lambda a, b: a % b)
|
|
391
|
+
_handle_binop_Mul = _make_binop(lambda a, b: a * b)
|
|
392
|
+
_handle_binop_Or = _make_binop(lambda a, b: a | b)
|
|
393
|
+
_handle_binop_Rol = _make_binop(lambda a, b: claripy.RotateLeft(a, zeroextend_on_demand(a, b)))
|
|
394
|
+
_handle_binop_Ror = _make_binop(lambda a, b: claripy.RotateRight(a, zeroextend_on_demand(a, b)))
|
|
395
|
+
_handle_binop_Sar = _make_binop(lambda a, b: a >> zeroextend_on_demand(a, b))
|
|
396
|
+
_handle_binop_Shl = _make_binop(lambda a, b: a << zeroextend_on_demand(a, b))
|
|
397
|
+
_handle_binop_Shr = _make_binop(lambda a, b: a.LShR(zeroextend_on_demand(a, b)))
|
|
398
|
+
_handle_binop_Sub = _make_binop(lambda a, b: a - b)
|
|
399
|
+
_handle_binop_Xor = _make_binop(lambda a, b: a ^ b)
|
|
400
|
+
|
|
401
|
+
def _handle_binop_Mull(self, expr):
|
|
402
|
+
a, b = self._expr(expr.operands[0]), self._expr(expr.operands[1])
|
|
403
|
+
if a is None or b is None:
|
|
404
|
+
return None
|
|
405
|
+
xt = a.size()
|
|
406
|
+
if expr.signed:
|
|
407
|
+
return a.sign_extend(xt) * b.sign_extend(xt)
|
|
408
|
+
return a.zero_extend(xt) * b.zero_extend(xt)
|
|
409
|
+
|
|
410
|
+
_handle_binop_AddF = _handle_binop_Default
|
|
411
|
+
_handle_binop_AddV = _handle_binop_Default
|
|
412
|
+
_handle_binop_Carry = _handle_binop_Default
|
|
413
|
+
_handle_binop_CmpF = _handle_binop_Default
|
|
414
|
+
_handle_binop_DivF = _handle_binop_Default
|
|
415
|
+
_handle_binop_DivV = _handle_binop_Default
|
|
416
|
+
_handle_binop_InterleaveLOV = _handle_binop_Default
|
|
417
|
+
_handle_binop_InterleaveHIV = _handle_binop_Default
|
|
418
|
+
_handle_binop_CasCmpEQ = _handle_binop_Default
|
|
419
|
+
_handle_binop_CasCmpNE = _handle_binop_Default
|
|
420
|
+
_handle_binop_ExpCmpNE = _handle_binop_Default
|
|
421
|
+
_handle_binop_SarNV = _handle_binop_Default
|
|
422
|
+
_handle_binop_ShrNV = _handle_binop_Default
|
|
423
|
+
_handle_binop_ShlNV = _handle_binop_Default
|
|
424
|
+
_handle_binop_CmpEQV = _handle_binop_Default
|
|
425
|
+
_handle_binop_CmpNEV = _handle_binop_Default
|
|
426
|
+
_handle_binop_CmpGEV = _handle_binop_Default
|
|
427
|
+
_handle_binop_CmpGTV = _handle_binop_Default
|
|
428
|
+
_handle_binop_CmpLEV = _handle_binop_Default
|
|
429
|
+
_handle_binop_CmpLTV = _handle_binop_Default
|
|
430
|
+
_handle_binop_MulF = _handle_binop_Default
|
|
431
|
+
_handle_binop_MulV = _handle_binop_Default
|
|
432
|
+
_handle_binop_MulHiV = _handle_binop_Default
|
|
433
|
+
_handle_binop_SBorrow = _handle_binop_Default
|
|
434
|
+
_handle_binop_SCarry = _handle_binop_Default
|
|
435
|
+
_handle_binop_SubF = _handle_binop_Default
|
|
436
|
+
_handle_binop_SubV = _handle_binop_Default
|
|
437
|
+
_handle_binop_MinV = _handle_binop_Default
|
|
438
|
+
_handle_binop_MaxV = _handle_binop_Default
|
|
439
|
+
_handle_binop_QAddV = _handle_binop_Default
|
|
440
|
+
_handle_binop_QNarrowBinV = _handle_binop_Default
|
|
441
|
+
_handle_binop_PermV = _handle_binop_Default
|
|
442
|
+
_handle_binop_Set = _handle_binop_Default
|
|
443
|
+
|
|
308
444
|
|
|
309
445
|
class InlineStringTransformationDescriptor:
|
|
310
446
|
"""
|
|
@@ -387,6 +523,7 @@ class InlinedStringTransformationSimplifier(OptimizationPass):
|
|
|
387
523
|
|
|
388
524
|
# remote the loop node
|
|
389
525
|
# since the loop node has exactly one external predecessor and one external successor, we can get rid of it
|
|
526
|
+
assert self.out_graph is not None
|
|
390
527
|
pred = next(iter(nn for nn in self.out_graph.predecessors(desc.loop_body) if nn is not desc.loop_body))
|
|
391
528
|
succ = next(iter(nn for nn in self.out_graph.successors(desc.loop_body) if nn is not desc.loop_body))
|
|
392
529
|
|
|
@@ -404,6 +541,7 @@ class InlinedStringTransformationSimplifier(OptimizationPass):
|
|
|
404
541
|
def _find_string_transformation_loops(self):
|
|
405
542
|
# find self loops
|
|
406
543
|
self_loops = []
|
|
544
|
+
assert self._graph is not None
|
|
407
545
|
for node in self._graph.nodes:
|
|
408
546
|
preds = list(self._graph.predecessors(node))
|
|
409
547
|
succs = list(self._graph.successors(node))
|
|
@@ -12,7 +12,7 @@ _l.addFilter(UniqueLogFilter())
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class ModSimplifierAILEngine(SimplifierAILEngine):
|
|
15
|
-
def
|
|
15
|
+
def _handle_binop_Sub(self, expr):
|
|
16
16
|
operand_0 = self._expr(expr.operands[0])
|
|
17
17
|
operand_1 = self._expr(expr.operands[1])
|
|
18
18
|
|
|
@@ -40,6 +40,8 @@ class ModSimplifierAILEngine(SimplifierAILEngine):
|
|
|
40
40
|
x_1 = operand_0
|
|
41
41
|
c_0 = operand_1.operands[1]
|
|
42
42
|
c_1 = operand_1.operands[0].operand.operands[1]
|
|
43
|
+
else:
|
|
44
|
+
assert False, "Unreachable"
|
|
43
45
|
|
|
44
46
|
if x_0 is not None and x_1 is not None and x_0.likes(x_1) and c_0.value == c_1.value:
|
|
45
47
|
return Expr.BinaryOp(expr.idx, "Mod", [x_0, c_0], expr.signed, **expr.tags)
|
|
@@ -70,7 +72,7 @@ class ModSimplifier(OptimizationPass):
|
|
|
70
72
|
super().__init__(func, **kwargs)
|
|
71
73
|
|
|
72
74
|
self.state = SimplifierAILState(self.project.arch)
|
|
73
|
-
self.engine = ModSimplifierAILEngine()
|
|
75
|
+
self.engine = ModSimplifierAILEngine(self.project)
|
|
74
76
|
|
|
75
77
|
self.analyze()
|
|
76
78
|
|
|
@@ -78,6 +80,7 @@ class ModSimplifier(OptimizationPass):
|
|
|
78
80
|
return True, None
|
|
79
81
|
|
|
80
82
|
def _analyze(self, cache=None):
|
|
83
|
+
assert self._graph is not None
|
|
81
84
|
for block in list(self._graph.nodes()):
|
|
82
85
|
new_block = block
|
|
83
86
|
old_block = None
|
|
@@ -5,15 +5,17 @@ from typing import Any, TYPE_CHECKING
|
|
|
5
5
|
from collections.abc import Generator
|
|
6
6
|
from enum import Enum
|
|
7
7
|
|
|
8
|
-
import networkx
|
|
8
|
+
import networkx
|
|
9
|
+
|
|
9
10
|
import ailment
|
|
10
11
|
|
|
11
12
|
from angr.analyses.decompiler import RegionIdentifier
|
|
12
13
|
from angr.analyses.decompiler.condition_processor import ConditionProcessor
|
|
13
|
-
from angr.analyses.decompiler.goto_manager import GotoManager
|
|
14
|
+
from angr.analyses.decompiler.goto_manager import Goto, GotoManager
|
|
14
15
|
from angr.analyses.decompiler.structuring import RecursiveStructurer, SAILRStructurer
|
|
15
16
|
from angr.analyses.decompiler.utils import add_labels
|
|
16
17
|
from angr.analyses.decompiler.counters import ControlFlowStructureCounter
|
|
18
|
+
from angr.project import Project
|
|
17
19
|
|
|
18
20
|
if TYPE_CHECKING:
|
|
19
21
|
from angr.knowledge_plugins.functions import Function
|
|
@@ -60,8 +62,10 @@ class BaseOptimizationPass:
|
|
|
60
62
|
|
|
61
63
|
ARCHES = [] # strings of supported architectures
|
|
62
64
|
PLATFORMS = [] # strings of supported platforms. Can be one of the following: "win32", "linux"
|
|
63
|
-
STAGE:
|
|
64
|
-
STRUCTURING: str | None =
|
|
65
|
+
STAGE: OptimizationPassStage # Specifies when this optimization pass should be executed
|
|
66
|
+
STRUCTURING: list[str] | None = (
|
|
67
|
+
None # specifies if this optimization pass is specific to a certain structuring algorithm
|
|
68
|
+
)
|
|
65
69
|
NAME = "N/A"
|
|
66
70
|
DESCRIPTION = "N/A"
|
|
67
71
|
|
|
@@ -69,7 +73,8 @@ class BaseOptimizationPass:
|
|
|
69
73
|
self._func: Function = func
|
|
70
74
|
|
|
71
75
|
@property
|
|
72
|
-
def project(self):
|
|
76
|
+
def project(self) -> Project:
|
|
77
|
+
assert self._func.project is not None
|
|
73
78
|
return self._func.project
|
|
74
79
|
|
|
75
80
|
@property
|
|
@@ -115,7 +120,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
115
120
|
variable_kb=None,
|
|
116
121
|
region_identifier=None,
|
|
117
122
|
reaching_definitions=None,
|
|
118
|
-
vvar_id_start=
|
|
123
|
+
vvar_id_start: int = 0,
|
|
119
124
|
entry_node_addr=None,
|
|
120
125
|
scratch: dict[str, Any] | None = None,
|
|
121
126
|
force_loop_single_exit: bool = True,
|
|
@@ -124,8 +129,8 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
124
129
|
):
|
|
125
130
|
super().__init__(func)
|
|
126
131
|
# self._blocks is just a cache
|
|
127
|
-
self._blocks_by_addr: dict[int, set[ailment.Block]] = blocks_by_addr
|
|
128
|
-
self._blocks_by_addr_and_idx: dict[tuple[int, int | None], ailment.Block] = blocks_by_addr_and_idx
|
|
132
|
+
self._blocks_by_addr: dict[int, set[ailment.Block]] = blocks_by_addr or {}
|
|
133
|
+
self._blocks_by_addr_and_idx: dict[tuple[int, int | None], ailment.Block] = blocks_by_addr_and_idx or {}
|
|
129
134
|
self._graph: networkx.DiGraph | None = graph
|
|
130
135
|
self._variable_kb = variable_kb
|
|
131
136
|
self._ri = region_identifier
|
|
@@ -164,10 +169,23 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
164
169
|
self._new_block_addrs.add(new_addr)
|
|
165
170
|
return new_addr
|
|
166
171
|
|
|
167
|
-
def _get_block(self, addr,
|
|
172
|
+
def _get_block(self, addr, **kwargs) -> ailment.Block | None:
|
|
173
|
+
"""
|
|
174
|
+
Get exactly one block by its address and optionally, also considering its block ID. An exception,
|
|
175
|
+
MultipleBlocksException, will be raised if there are more than one block satisfying the specified criteria.
|
|
176
|
+
|
|
177
|
+
:param addr: The address of the block.
|
|
178
|
+
:param kwargs: Optionally, you can specify "idx" to consider the block ID. If "idx" is not specified, this
|
|
179
|
+
method will return the only block at the specified address, None if there is no block at
|
|
180
|
+
that address, or raise an exception if there are more than one block at that address.
|
|
181
|
+
:return: The requested block or None if no block matching the specified criteria exists.
|
|
182
|
+
"""
|
|
183
|
+
|
|
168
184
|
if not self._blocks_by_addr:
|
|
169
185
|
return None
|
|
170
|
-
|
|
186
|
+
idx_specified = "idx" in kwargs
|
|
187
|
+
idx = kwargs.get("idx")
|
|
188
|
+
if not idx_specified:
|
|
171
189
|
blocks = self._blocks_by_addr.get(addr, None)
|
|
172
190
|
else:
|
|
173
191
|
blocks = [self._blocks_by_addr_and_idx.get((addr, idx), None)]
|
|
@@ -175,8 +193,12 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
175
193
|
return None
|
|
176
194
|
if len(blocks) == 1:
|
|
177
195
|
return next(iter(blocks))
|
|
196
|
+
if idx_specified:
|
|
197
|
+
raise MultipleBlocksException(
|
|
198
|
+
f"There are {len(blocks)} blocks at address {addr:#x}.{idx} but only one is requested."
|
|
199
|
+
)
|
|
178
200
|
raise MultipleBlocksException(
|
|
179
|
-
"There are
|
|
201
|
+
f"There are {len(blocks)} blocks at address {addr:#x} (block ID ignored) but only one is requested."
|
|
180
202
|
)
|
|
181
203
|
|
|
182
204
|
def _get_blocks(self, addr, idx=None) -> Generator[ailment.Block]:
|
|
@@ -195,6 +217,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
195
217
|
def _update_block(self, old_block, new_block):
|
|
196
218
|
if self.out_graph is None:
|
|
197
219
|
self.out_graph = self._graph # we do not make copy here for performance reasons. we can change it if needed
|
|
220
|
+
assert self.out_graph is not None
|
|
198
221
|
|
|
199
222
|
if old_block not in self.out_graph:
|
|
200
223
|
return
|
|
@@ -220,6 +243,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
220
243
|
def _remove_block(self, block):
|
|
221
244
|
if self.out_graph is None:
|
|
222
245
|
self.out_graph = self._graph
|
|
246
|
+
assert self.out_graph is not None
|
|
223
247
|
|
|
224
248
|
if block in self.out_graph:
|
|
225
249
|
self.out_graph.remove_node(block)
|
|
@@ -270,10 +294,6 @@ class SequenceOptimizationPass(BaseOptimizationPass):
|
|
|
270
294
|
The base class for any sequence node optimization pass.
|
|
271
295
|
"""
|
|
272
296
|
|
|
273
|
-
ARCHES = [] # strings of supported architectures
|
|
274
|
-
PLATFORMS = [] # strings of supported platforms. Can be one of the following: "win32", "linux"
|
|
275
|
-
STAGE: int = None # Specifies when this optimization pass should be executed
|
|
276
|
-
|
|
277
297
|
def __init__(self, func, seq=None, **kwargs):
|
|
278
298
|
super().__init__(func)
|
|
279
299
|
self.seq = seq
|
|
@@ -298,6 +318,10 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
298
318
|
STRUCTURING = [SAILRStructurer.NAME]
|
|
299
319
|
STAGE = OptimizationPassStage.DURING_REGION_IDENTIFICATION
|
|
300
320
|
|
|
321
|
+
_initial_gotos: set[Goto]
|
|
322
|
+
_goto_manager: GotoManager
|
|
323
|
+
_prev_graph: networkx.DiGraph
|
|
324
|
+
|
|
301
325
|
def __init__(
|
|
302
326
|
self,
|
|
303
327
|
func,
|
|
@@ -321,10 +345,6 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
321
345
|
self._must_improve_rel_quality = must_improve_rel_quality
|
|
322
346
|
self._readd_labels = readd_labels
|
|
323
347
|
|
|
324
|
-
self._initial_gotos = None
|
|
325
|
-
self._goto_manager: GotoManager | None = None
|
|
326
|
-
self._prev_graph: networkx.DiGraph | None = None
|
|
327
|
-
|
|
328
348
|
# relative quality metrics (excludes gotos)
|
|
329
349
|
self._initial_structure_counter = None
|
|
330
350
|
self._current_structure_counter = None
|
|
@@ -13,6 +13,7 @@ from .const_mull_a_shift import ConstMullAShift
|
|
|
13
13
|
from .extended_byte_and_mask import ExtendedByteAndMask
|
|
14
14
|
from .remove_empty_if_body import RemoveEmptyIfBody
|
|
15
15
|
from .remove_redundant_ite_branch import RemoveRedundantITEBranches
|
|
16
|
+
from .shl_to_mul import ShlToMul
|
|
16
17
|
from .single_bit_xor import SingleBitXor
|
|
17
18
|
from .a_sub_a_sub_n import ASubASubN
|
|
18
19
|
from .conv_a_sub0_shr_and import ConvASub0ShrAnd
|
|
@@ -45,13 +46,15 @@ from .inlined_strcpy_consolidation import InlinedStrcpyConsolidation
|
|
|
45
46
|
from .inlined_wstrcpy import InlinedWstrcpy
|
|
46
47
|
from .cmpord_rewriter import CmpORDRewriter
|
|
47
48
|
from .coalesce_adjacent_shrs import CoalesceAdjacentShiftRights
|
|
48
|
-
|
|
49
|
+
from .a_mul_const_sub_a import AMulConstSubA
|
|
49
50
|
from .base import PeepholeOptimizationExprBase, PeepholeOptimizationStmtBase, PeepholeOptimizationMultiStmtBase
|
|
50
51
|
|
|
52
|
+
|
|
51
53
|
ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
52
54
|
ADivConstAddAMulNDivConst,
|
|
53
55
|
AMulConstDivShrConst,
|
|
54
56
|
AShlConstSubA,
|
|
57
|
+
AMulConstSubA,
|
|
55
58
|
ASubADiv,
|
|
56
59
|
ASubADivConstMulConst,
|
|
57
60
|
ARMCmpF,
|
|
@@ -94,6 +97,7 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
|
94
97
|
InlinedWstrcpy,
|
|
95
98
|
CmpORDRewriter,
|
|
96
99
|
CoalesceAdjacentShiftRights,
|
|
100
|
+
ShlToMul,
|
|
97
101
|
]
|
|
98
102
|
|
|
99
103
|
MULTI_STMT_OPTS: list[type[PeepholeOptimizationMultiStmtBase]] = [
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# pylint:disable=missing-class-docstring,no-self-use,too-many-boolean-expressions
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from ailment.expression import BinaryOp, Const
|
|
4
|
+
|
|
5
|
+
from .base import PeepholeOptimizationExprBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AMulConstSubA(PeepholeOptimizationExprBase):
|
|
9
|
+
__slots__ = ()
|
|
10
|
+
|
|
11
|
+
NAME = "a * N - a => a * (N - 1)"
|
|
12
|
+
expr_classes = (BinaryOp,)
|
|
13
|
+
|
|
14
|
+
def optimize(self, expr: BinaryOp, **kwargs):
|
|
15
|
+
if (
|
|
16
|
+
expr.op == "Sub"
|
|
17
|
+
and len(expr.operands) == 2
|
|
18
|
+
and isinstance(expr.operands[0], BinaryOp)
|
|
19
|
+
and expr.operands[0].op == "Mul"
|
|
20
|
+
and isinstance(expr.operands[0].operands[1], Const)
|
|
21
|
+
and expr.signed == expr.operands[0].signed
|
|
22
|
+
):
|
|
23
|
+
a = expr.operands[1]
|
|
24
|
+
if expr.operands[0].operands[0].likes(a):
|
|
25
|
+
N = expr.operands[0].operands[1].value
|
|
26
|
+
return BinaryOp(
|
|
27
|
+
expr.idx,
|
|
28
|
+
"Mul",
|
|
29
|
+
[a, Const(None, None, N - 1, expr.bits, **expr.operands[0].operands[1].tags)],
|
|
30
|
+
expr.signed,
|
|
31
|
+
**expr.tags,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return None
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# pylint:disable=missing-class-docstring,no-self-use
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
from ailment.expression import BinaryOp, Const
|
|
3
4
|
|
|
@@ -17,6 +18,7 @@ class AShlConstSubA(PeepholeOptimizationExprBase):
|
|
|
17
18
|
and isinstance(expr.operands[0], BinaryOp)
|
|
18
19
|
and expr.operands[0].op == "Shl"
|
|
19
20
|
and isinstance(expr.operands[0].operands[1], Const)
|
|
21
|
+
and expr.signed == expr.operands[0].signed
|
|
20
22
|
):
|
|
21
23
|
a = expr.operands[1]
|
|
22
24
|
if expr.operands[0].operands[0].likes(a):
|
|
@@ -25,7 +27,7 @@ class AShlConstSubA(PeepholeOptimizationExprBase):
|
|
|
25
27
|
expr.idx,
|
|
26
28
|
"Mul",
|
|
27
29
|
[a, Const(None, None, 2**N - 1, expr.bits, **expr.operands[0].operands[1].tags)],
|
|
28
|
-
|
|
30
|
+
expr.signed,
|
|
29
31
|
**expr.tags,
|
|
30
32
|
)
|
|
31
33
|
|