angr 9.2.130__py3-none-macosx_11_0_arm64.whl → 9.2.131__py3-none-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/analyses/decompiler/clinic.py +283 -4
- angr/analyses/decompiler/optimization_passes/__init__.py +0 -3
- 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 +15 -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_engine.py +2 -2
- angr/analyses/decompiler/structured_codegen/c.py +74 -13
- angr/analyses/decompiler/structuring/phoenix.py +14 -5
- angr/analyses/s_propagator.py +20 -2
- angr/analyses/typehoon/simple_solver.py +9 -2
- angr/analyses/typehoon/typehoon.py +4 -1
- angr/analyses/variable_recovery/engine_ail.py +15 -15
- angr/analyses/variable_recovery/engine_base.py +3 -0
- angr/engines/light/engine.py +3 -3
- angr/engines/vex/claripy/irop.py +13 -2
- angr/lib/angr_native.dylib +0 -0
- angr/utils/bits.py +5 -0
- angr/utils/formatting.py +4 -1
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/METADATA +6 -6
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/RECORD +35 -33
- angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -223
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/LICENSE +0 -0
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/WHEEL +0 -0
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/entry_points.txt +0 -0
- {angr-9.2.130.dist-info → angr-9.2.131.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ from angr.knowledge_plugins.functions import Function
|
|
|
18
18
|
from angr.knowledge_plugins.cfg.memory_data import MemoryDataSort
|
|
19
19
|
from angr.codenode import BlockNode
|
|
20
20
|
from angr.utils import timethis
|
|
21
|
+
from angr.utils.graph import GraphUtils
|
|
21
22
|
from angr.calling_conventions import SimRegArg, SimStackArg, SimFunctionArgument
|
|
22
23
|
from angr.sim_type import (
|
|
23
24
|
SimTypeChar,
|
|
@@ -115,6 +116,7 @@ class Clinic(Analysis):
|
|
|
115
116
|
desired_variables: set[str] | None = None,
|
|
116
117
|
force_loop_single_exit: bool = True,
|
|
117
118
|
complete_successors: bool = False,
|
|
119
|
+
unsound_fix_abnormal_switches: bool = True,
|
|
118
120
|
):
|
|
119
121
|
if not func.normalized and mode == ClinicMode.DECOMPILE:
|
|
120
122
|
raise ValueError("Decompilation must work on normalized function graphs.")
|
|
@@ -146,6 +148,7 @@ class Clinic(Analysis):
|
|
|
146
148
|
self._must_struct = must_struct
|
|
147
149
|
self._reset_variable_names = reset_variable_names
|
|
148
150
|
self._rewrite_ites_to_diamonds = rewrite_ites_to_diamonds
|
|
151
|
+
self._unsound_fix_abnormal_switches = unsound_fix_abnormal_switches
|
|
149
152
|
self.reaching_definitions: ReachingDefinitionsAnalysis | None = None
|
|
150
153
|
self._cache = cache
|
|
151
154
|
self._mode = mode
|
|
@@ -265,6 +268,8 @@ class Clinic(Analysis):
|
|
|
265
268
|
def _decompilation_fixups(self, ail_graph):
|
|
266
269
|
is_pcode_arch = ":" in self.project.arch.name
|
|
267
270
|
|
|
271
|
+
# _fix_abnormal_switch_case_heads may re-lift from VEX blocks, so it should be placed as high up as possible
|
|
272
|
+
self._fix_abnormal_switch_case_heads(ail_graph)
|
|
268
273
|
if self._rewrite_ites_to_diamonds:
|
|
269
274
|
self._rewrite_ite_expressions(ail_graph)
|
|
270
275
|
self._remove_redundant_jump_blocks(ail_graph)
|
|
@@ -941,7 +946,7 @@ class Clinic(Analysis):
|
|
|
941
946
|
|
|
942
947
|
def _convert(self, block_node):
|
|
943
948
|
"""
|
|
944
|
-
Convert a
|
|
949
|
+
Convert a BlockNode to an AIL block.
|
|
945
950
|
|
|
946
951
|
:param block_node: A BlockNode instance.
|
|
947
952
|
:return: A converted AIL block.
|
|
@@ -955,13 +960,16 @@ class Clinic(Analysis):
|
|
|
955
960
|
return ailment.Block(block_node.addr, 0, statements=[])
|
|
956
961
|
|
|
957
962
|
block = self.project.factory.block(block_node.addr, block_node.size, cross_insn_opt=False)
|
|
963
|
+
return self._convert_vex(block)
|
|
964
|
+
|
|
965
|
+
def _convert_vex(self, block):
|
|
958
966
|
if block.vex.jumpkind not in {"Ijk_Call", "Ijk_Boring", "Ijk_Ret"} and not block.vex.jumpkind.startswith(
|
|
959
967
|
"Ijk_Sys"
|
|
960
968
|
):
|
|
961
969
|
# we don't support lifting this block. use a dummy block instead
|
|
962
970
|
dirty_expr = ailment.Expr.DirtyExpression(
|
|
963
971
|
self._ail_manager.next_atom,
|
|
964
|
-
f"Unsupported jumpkind {block.vex.jumpkind} at address {
|
|
972
|
+
f"Unsupported jumpkind {block.vex.jumpkind} at address {block.addr}",
|
|
965
973
|
[],
|
|
966
974
|
bits=0,
|
|
967
975
|
)
|
|
@@ -969,10 +977,10 @@ class Clinic(Analysis):
|
|
|
969
977
|
ailment.Stmt.DirtyStatement(
|
|
970
978
|
self._ail_manager.next_atom(),
|
|
971
979
|
dirty_expr,
|
|
972
|
-
ins_addr=
|
|
980
|
+
ins_addr=block.addr,
|
|
973
981
|
)
|
|
974
982
|
]
|
|
975
|
-
return ailment.Block(
|
|
983
|
+
return ailment.Block(block.addr, block.size, statements=statements)
|
|
976
984
|
|
|
977
985
|
return ailment.IRSBConverter.convert(block.vex, self._ail_manager)
|
|
978
986
|
|
|
@@ -2055,6 +2063,277 @@ class Clinic(Analysis):
|
|
|
2055
2063
|
|
|
2056
2064
|
return end_block_ail.addr
|
|
2057
2065
|
|
|
2066
|
+
def _fix_abnormal_switch_case_heads(self, ail_graph: networkx.DiGraph) -> None:
|
|
2067
|
+
"""
|
|
2068
|
+
Detect the existence of switch-case heads whose indirect jump node has more than one predecessor, and attempt
|
|
2069
|
+
to fix those cases by altering the graph.
|
|
2070
|
+
"""
|
|
2071
|
+
|
|
2072
|
+
if self._cfg is None:
|
|
2073
|
+
return
|
|
2074
|
+
|
|
2075
|
+
if not self._cfg.jump_tables:
|
|
2076
|
+
return
|
|
2077
|
+
|
|
2078
|
+
node_dict: defaultdict[int, list[ailment.Block]] = defaultdict(list)
|
|
2079
|
+
for node in ail_graph:
|
|
2080
|
+
node_dict[node.addr].append(node)
|
|
2081
|
+
|
|
2082
|
+
candidates = []
|
|
2083
|
+
for block_addr in self._cfg.jump_tables:
|
|
2084
|
+
block_nodes = node_dict[block_addr]
|
|
2085
|
+
for block_node in block_nodes:
|
|
2086
|
+
if ail_graph.in_degree[block_node] > 1:
|
|
2087
|
+
# found it
|
|
2088
|
+
candidates.append(block_node)
|
|
2089
|
+
|
|
2090
|
+
if not candidates:
|
|
2091
|
+
return
|
|
2092
|
+
|
|
2093
|
+
sorted_nodes = GraphUtils.quasi_topological_sort_nodes(ail_graph)
|
|
2094
|
+
node_to_rank = {node: rank for rank, node in enumerate(sorted_nodes)}
|
|
2095
|
+
for candidate in candidates:
|
|
2096
|
+
# determine the "intended" switch-case head using topological order
|
|
2097
|
+
preds = list(ail_graph.predecessors(candidate))
|
|
2098
|
+
preds = sorted(preds, key=lambda n_: node_to_rank[n_])
|
|
2099
|
+
intended_head = preds[0]
|
|
2100
|
+
other_heads = preds[1:]
|
|
2101
|
+
|
|
2102
|
+
# now here is the tricky part. there are two cases:
|
|
2103
|
+
# Case 1: the intended head and the other heads share the same suffix (of instructions)
|
|
2104
|
+
# Example:
|
|
2105
|
+
# ; binary 736cb27201273f6c4f83da362c9595b50d12333362e02bc7a77dd327cc6b045a
|
|
2106
|
+
# 0041DA97 mov ecx, [esp+2Ch+var_18] ; this is the intended head
|
|
2107
|
+
# 0041DA9B mov ecx, [ecx]
|
|
2108
|
+
# 0041DA9D cmp ecx, 9
|
|
2109
|
+
# 0041DAA0 jbe loc_41D5A8
|
|
2110
|
+
#
|
|
2111
|
+
# 0041D599 mov ecx, [ecx] ; this is the other head
|
|
2112
|
+
# 0041D59B mov [esp+2Ch+var_10], eax
|
|
2113
|
+
# 0041D59F cmp ecx, 9
|
|
2114
|
+
# 0041D5A2 ja loc_41DAA6 ; fallthrough to 0x41d5a8
|
|
2115
|
+
# given the overlap of two instructions at the end of both blocks, we will alter the second block to remove
|
|
2116
|
+
# the overlapped instructions and add an unconditional jump so that it jumps to 0x41da9d.
|
|
2117
|
+
# this is the most common case created by jump threading optimization in compilers. it's easy to handle.
|
|
2118
|
+
|
|
2119
|
+
# Case 2: the intended head and the other heads do not share the same suffix of instructions. in this case,
|
|
2120
|
+
# we cannot reliably convert the blocks into a properly structured switch-case construct. we will alter the
|
|
2121
|
+
# last instruction of all other heads to jump to the cmp instruction in the intended head, but do not remove
|
|
2122
|
+
# any other instructions in these other heads. this is unsound, but is the best we can do in this case.
|
|
2123
|
+
|
|
2124
|
+
overlaps = [self._get_overlapping_suffix_instructions(intended_head, head) for head in other_heads]
|
|
2125
|
+
if overlaps and (overlap := min(overlaps)) > 0:
|
|
2126
|
+
# Case 1
|
|
2127
|
+
self._fix_abnormal_switch_case_heads_case1(ail_graph, candidate, intended_head, other_heads, overlap)
|
|
2128
|
+
else:
|
|
2129
|
+
if self._unsound_fix_abnormal_switches:
|
|
2130
|
+
# Case 2
|
|
2131
|
+
l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
|
|
2132
|
+
# find the comparison instruction in the intended head
|
|
2133
|
+
comparison_stmt = None
|
|
2134
|
+
if "cc_op" in self.project.arch.registers:
|
|
2135
|
+
comparison_stmt = next(
|
|
2136
|
+
iter(
|
|
2137
|
+
stmt
|
|
2138
|
+
for stmt in intended_head.statements
|
|
2139
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2140
|
+
and isinstance(stmt.dst, ailment.Expr.Register)
|
|
2141
|
+
and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
|
|
2142
|
+
),
|
|
2143
|
+
None,
|
|
2144
|
+
)
|
|
2145
|
+
if comparison_stmt is not None:
|
|
2146
|
+
intended_head_block = self.project.factory.block(
|
|
2147
|
+
intended_head.addr, size=intended_head.original_size
|
|
2148
|
+
)
|
|
2149
|
+
cmp_rpos = len(
|
|
2150
|
+
intended_head_block.instruction_addrs
|
|
2151
|
+
) - intended_head_block.instruction_addrs.index(comparison_stmt.ins_addr)
|
|
2152
|
+
else:
|
|
2153
|
+
cmp_rpos = 2
|
|
2154
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2155
|
+
ail_graph,
|
|
2156
|
+
candidate,
|
|
2157
|
+
intended_head,
|
|
2158
|
+
other_heads,
|
|
2159
|
+
intended_head_split_insns=cmp_rpos,
|
|
2160
|
+
other_head_split_insns=0,
|
|
2161
|
+
)
|
|
2162
|
+
|
|
2163
|
+
def _get_overlapping_suffix_instructions(self, ailblock_0: ailment.Block, ailblock_1: ailment.Block) -> int:
|
|
2164
|
+
# we first compare their ending conditional jumps
|
|
2165
|
+
if not self._get_overlapping_suffix_instructions_compare_conditional_jumps(ailblock_0, ailblock_1):
|
|
2166
|
+
return 0
|
|
2167
|
+
|
|
2168
|
+
# we re-lift the blocks and compare the instructions
|
|
2169
|
+
block_0 = self.project.factory.block(ailblock_0.addr, size=ailblock_0.original_size)
|
|
2170
|
+
block_1 = self.project.factory.block(ailblock_1.addr, size=ailblock_1.original_size)
|
|
2171
|
+
|
|
2172
|
+
i0 = len(block_0.capstone.insns) - 2
|
|
2173
|
+
i1 = len(block_1.capstone.insns) - 2
|
|
2174
|
+
overlap = 1
|
|
2175
|
+
while i0 >= 0 and i1 >= 0:
|
|
2176
|
+
same = self._get_overlapping_suffix_instructions_compare_instructions(
|
|
2177
|
+
block_0.capstone.insns[i0], block_1.capstone.insns[i1]
|
|
2178
|
+
)
|
|
2179
|
+
if not same:
|
|
2180
|
+
break
|
|
2181
|
+
overlap += 1
|
|
2182
|
+
i0 -= 1
|
|
2183
|
+
i1 -= 1
|
|
2184
|
+
|
|
2185
|
+
return overlap
|
|
2186
|
+
|
|
2187
|
+
@staticmethod
|
|
2188
|
+
def _get_overlapping_suffix_instructions_compare_instructions(insn_0, insn_1) -> bool:
|
|
2189
|
+
return insn_0.mnemonic == insn_1.mnemonic and insn_0.op_str == insn_1.op_str
|
|
2190
|
+
|
|
2191
|
+
@staticmethod
|
|
2192
|
+
def _get_overlapping_suffix_instructions_compare_conditional_jumps(
|
|
2193
|
+
ailblock_0: ailment.Block, ailblock_1: ailment.Block
|
|
2194
|
+
) -> bool:
|
|
2195
|
+
# TODO: The logic here is naive and highly customized to the only example I can access. Expand this method
|
|
2196
|
+
# later to handle more cases if needed.
|
|
2197
|
+
if len(ailblock_0.statements) == 0 or len(ailblock_1.statements) == 0:
|
|
2198
|
+
return False
|
|
2199
|
+
|
|
2200
|
+
# 12 | 0x41d5a2 | t17 = (t4 <= 0x9<32>)
|
|
2201
|
+
# 13 | 0x41d5a2 | t16 = Conv(1->32, t17)
|
|
2202
|
+
# 14 | 0x41d5a2 | t14 = t16
|
|
2203
|
+
# 15 | 0x41d5a2 | t18 = Conv(32->1, t14)
|
|
2204
|
+
# 16 | 0x41d5a2 | t9 = t18
|
|
2205
|
+
# 17 | 0x41d5a2 | if (t9) { Goto 0x41d5a8<32> } else { Goto 0x41daa6<32> }
|
|
2206
|
+
|
|
2207
|
+
last_stmt_0 = ailblock_0.statements[-1]
|
|
2208
|
+
last_stmt_1 = ailblock_1.statements[-1]
|
|
2209
|
+
if not (isinstance(last_stmt_0, ailment.Stmt.ConditionalJump) and last_stmt_0.likes(last_stmt_1)):
|
|
2210
|
+
return False
|
|
2211
|
+
|
|
2212
|
+
last_cmp_stmt_0 = next(
|
|
2213
|
+
iter(
|
|
2214
|
+
stmt
|
|
2215
|
+
for stmt in reversed(ailblock_0.statements)
|
|
2216
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2217
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2218
|
+
and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
|
|
2219
|
+
and isinstance(stmt.src.operands[1], ailment.Expr.Const)
|
|
2220
|
+
and stmt.ins_addr == last_stmt_0.ins_addr
|
|
2221
|
+
),
|
|
2222
|
+
None,
|
|
2223
|
+
)
|
|
2224
|
+
last_cmp_stmt_1 = next(
|
|
2225
|
+
iter(
|
|
2226
|
+
stmt
|
|
2227
|
+
for stmt in reversed(ailblock_1.statements)
|
|
2228
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2229
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2230
|
+
and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
|
|
2231
|
+
and isinstance(stmt.src.operands[1], ailment.Expr.Const)
|
|
2232
|
+
and stmt.ins_addr == last_stmt_1.ins_addr
|
|
2233
|
+
),
|
|
2234
|
+
None,
|
|
2235
|
+
)
|
|
2236
|
+
return (
|
|
2237
|
+
last_cmp_stmt_0 is not None
|
|
2238
|
+
and last_cmp_stmt_1 is not None
|
|
2239
|
+
and last_cmp_stmt_0.src.op == last_cmp_stmt_1.src.op
|
|
2240
|
+
and last_cmp_stmt_0.src.operands[1].likes(last_cmp_stmt_1.src.operands[1])
|
|
2241
|
+
)
|
|
2242
|
+
|
|
2243
|
+
def _fix_abnormal_switch_case_heads_case1(
|
|
2244
|
+
self,
|
|
2245
|
+
ail_graph: networkx.DiGraph,
|
|
2246
|
+
indirect_jump_node: ailment.Block,
|
|
2247
|
+
intended_head: ailment.Block,
|
|
2248
|
+
other_heads: list[ailment.Block],
|
|
2249
|
+
overlap: int,
|
|
2250
|
+
) -> None:
|
|
2251
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2252
|
+
ail_graph,
|
|
2253
|
+
indirect_jump_node,
|
|
2254
|
+
intended_head,
|
|
2255
|
+
other_heads,
|
|
2256
|
+
intended_head_split_insns=overlap,
|
|
2257
|
+
other_head_split_insns=overlap,
|
|
2258
|
+
)
|
|
2259
|
+
|
|
2260
|
+
def _fix_abnormal_switch_case_heads_case2(
|
|
2261
|
+
self,
|
|
2262
|
+
ail_graph: networkx.DiGraph,
|
|
2263
|
+
indirect_jump_node: ailment.Block,
|
|
2264
|
+
intended_head: ailment.Block,
|
|
2265
|
+
other_heads: list[ailment.Block],
|
|
2266
|
+
intended_head_split_insns: int = 1,
|
|
2267
|
+
other_head_split_insns: int = 0,
|
|
2268
|
+
) -> None:
|
|
2269
|
+
|
|
2270
|
+
# split the intended head into two
|
|
2271
|
+
intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
|
|
2272
|
+
split_ins_addr = intended_head_block.instruction_addrs[-intended_head_split_insns]
|
|
2273
|
+
intended_head_block_0 = self.project.factory.block(intended_head.addr, size=split_ins_addr - intended_head.addr)
|
|
2274
|
+
intended_head_block_1 = self.project.factory.block(
|
|
2275
|
+
split_ins_addr, size=intended_head.addr + intended_head.original_size - split_ins_addr
|
|
2276
|
+
)
|
|
2277
|
+
intended_head_0 = self._convert_vex(intended_head_block_0)
|
|
2278
|
+
intended_head_1 = self._convert_vex(intended_head_block_1)
|
|
2279
|
+
|
|
2280
|
+
# adjust the graph accordingly
|
|
2281
|
+
preds = list(ail_graph.predecessors(intended_head))
|
|
2282
|
+
succs = list(ail_graph.successors(intended_head))
|
|
2283
|
+
ail_graph.remove_node(intended_head)
|
|
2284
|
+
ail_graph.add_edge(intended_head_0, intended_head_1)
|
|
2285
|
+
for pred in preds:
|
|
2286
|
+
if pred is intended_head:
|
|
2287
|
+
ail_graph.add_edge(intended_head_1, intended_head_0)
|
|
2288
|
+
else:
|
|
2289
|
+
ail_graph.add_edge(pred, intended_head_0)
|
|
2290
|
+
for succ in succs:
|
|
2291
|
+
if succ is intended_head:
|
|
2292
|
+
ail_graph.add_edge(intended_head_1, intended_head_0)
|
|
2293
|
+
else:
|
|
2294
|
+
ail_graph.add_edge(intended_head_1, succ)
|
|
2295
|
+
|
|
2296
|
+
# split other heads
|
|
2297
|
+
for o in other_heads:
|
|
2298
|
+
if other_head_split_insns > 0:
|
|
2299
|
+
o_block = self.project.factory.block(o.addr, size=o.original_size)
|
|
2300
|
+
o_split_addr = o_block.instruction_addrs[-other_head_split_insns]
|
|
2301
|
+
new_o_block = self.project.factory.block(o.addr, size=o_split_addr - o.addr)
|
|
2302
|
+
new_head = self._convert_vex(new_o_block)
|
|
2303
|
+
else:
|
|
2304
|
+
new_head = o
|
|
2305
|
+
|
|
2306
|
+
if (
|
|
2307
|
+
new_head.statements
|
|
2308
|
+
and isinstance(new_head.statements[-1], ailment.Stmt.Jump)
|
|
2309
|
+
and isinstance(new_head.statements[-1].target, ailment.Expr.Const)
|
|
2310
|
+
):
|
|
2311
|
+
# update the jump target
|
|
2312
|
+
new_head.statements[-1] = ailment.Stmt.Jump(
|
|
2313
|
+
new_head.statements[-1].idx,
|
|
2314
|
+
ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
|
|
2315
|
+
target_idx=intended_head_1.idx,
|
|
2316
|
+
**new_head.statements[-1].tags,
|
|
2317
|
+
)
|
|
2318
|
+
|
|
2319
|
+
# adjust the graph accordingly
|
|
2320
|
+
preds = list(ail_graph.predecessors(o))
|
|
2321
|
+
succs = list(ail_graph.successors(o))
|
|
2322
|
+
ail_graph.remove_node(o)
|
|
2323
|
+
for pred in preds:
|
|
2324
|
+
if pred is o:
|
|
2325
|
+
ail_graph.add_edge(new_head, new_head)
|
|
2326
|
+
else:
|
|
2327
|
+
ail_graph.add_edge(pred, new_head)
|
|
2328
|
+
for succ in succs:
|
|
2329
|
+
if succ is o:
|
|
2330
|
+
ail_graph.add_edge(new_head, new_head)
|
|
2331
|
+
elif succ is indirect_jump_node:
|
|
2332
|
+
ail_graph.add_edge(new_head, intended_head_1)
|
|
2333
|
+
else:
|
|
2334
|
+
# it should be going to the default node. ignore it
|
|
2335
|
+
pass
|
|
2336
|
+
|
|
2058
2337
|
@staticmethod
|
|
2059
2338
|
def _remove_redundant_jump_blocks(ail_graph):
|
|
2060
2339
|
def first_conditional_jump(block: ailment.Block) -> ailment.Stmt.ConditionalJump | None:
|
|
@@ -11,7 +11,6 @@ from .expr_op_swapper import ExprOpSwapper
|
|
|
11
11
|
from .ite_region_converter import ITERegionConverter
|
|
12
12
|
from .ite_expr_converter import ITEExprConverter
|
|
13
13
|
from .lowered_switch_simplifier import LoweredSwitchSimplifier
|
|
14
|
-
from .multi_simplifier import MultiSimplifier
|
|
15
14
|
from .div_simplifier import DivSimplifier
|
|
16
15
|
from .mod_simplifier import ModSimplifier
|
|
17
16
|
from .return_duplicator_low import ReturnDuplicatorLow
|
|
@@ -45,7 +44,6 @@ ALL_OPTIMIZATION_PASSES = [
|
|
|
45
44
|
WinStackCanarySimplifier,
|
|
46
45
|
BasePointerSaveSimplifier,
|
|
47
46
|
DivSimplifier,
|
|
48
|
-
MultiSimplifier,
|
|
49
47
|
ModSimplifier,
|
|
50
48
|
ConstantDereferencesSimplifier,
|
|
51
49
|
RetAddrSaveSimplifier,
|
|
@@ -116,7 +114,6 @@ __all__ = (
|
|
|
116
114
|
"ITERegionConverter",
|
|
117
115
|
"ITEExprConverter",
|
|
118
116
|
"LoweredSwitchSimplifier",
|
|
119
|
-
"MultiSimplifier",
|
|
120
117
|
"DivSimplifier",
|
|
121
118
|
"ModSimplifier",
|
|
122
119
|
"ReturnDuplicatorLow",
|
|
@@ -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
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# pylint:disable=missing-class-docstring,no-self-use
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
from ailment.expression import BinaryOp, Const, Expression, Convert
|
|
3
4
|
from ailment.statement import Call
|
|
4
5
|
|
|
5
6
|
from .base import PeepholeOptimizationExprBase
|
|
7
|
+
from .utils import get_expr_shift_left_amount
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class Bswap(PeepholeOptimizationExprBase):
|
|
@@ -69,19 +71,21 @@ class Bswap(PeepholeOptimizationExprBase):
|
|
|
69
71
|
cores = set()
|
|
70
72
|
for piece in or_pieces:
|
|
71
73
|
if isinstance(piece, BinaryOp):
|
|
72
|
-
if piece.op
|
|
74
|
+
if piece.op in {"Shl", "Mul"} and isinstance(piece.operands[1], Const):
|
|
73
75
|
cores.add(piece.operands[0])
|
|
74
|
-
|
|
76
|
+
shift_amount = get_expr_shift_left_amount(piece)
|
|
77
|
+
shifts.add(("<<", shift_amount, 0xFFFFFFFF))
|
|
75
78
|
elif piece.op == "And" and isinstance(piece.operands[1], Const):
|
|
76
79
|
and_amount = piece.operands[1].value
|
|
77
80
|
and_core = piece.operands[0]
|
|
78
81
|
if (
|
|
79
82
|
isinstance(and_core, BinaryOp)
|
|
80
|
-
and and_core.op
|
|
83
|
+
and and_core.op in {"Shl", "Mul"}
|
|
81
84
|
and isinstance(and_core.operands[1], Const)
|
|
82
85
|
):
|
|
83
86
|
cores.add(and_core.operands[0])
|
|
84
|
-
|
|
87
|
+
shift_amount = get_expr_shift_left_amount(and_core)
|
|
88
|
+
shifts.add(("<<", shift_amount, and_amount))
|
|
85
89
|
elif (
|
|
86
90
|
isinstance(and_core, BinaryOp)
|
|
87
91
|
and and_core.op == "Shr"
|
|
@@ -112,9 +116,9 @@ class Bswap(PeepholeOptimizationExprBase):
|
|
|
112
116
|
if (
|
|
113
117
|
(
|
|
114
118
|
isinstance(inner_first, BinaryOp)
|
|
115
|
-
and inner_first.op
|
|
119
|
+
and inner_first.op in {"Shl", "Mul"}
|
|
116
120
|
and isinstance(inner_first.operands[1], Const)
|
|
117
|
-
and inner_first
|
|
121
|
+
and get_expr_shift_left_amount(inner_first) == 8
|
|
118
122
|
)
|
|
119
123
|
and (
|
|
120
124
|
isinstance(inner_second, BinaryOp)
|