4 from graph
import Graph
10 _log
= logging
.getLogger(__name__
)
13 def split_node(cfg
, n
):
14 """Split node "horizontally", by duplicating its content and splitting
15 incoming and outgoing edges.
17 assert cfg
.degree_in(n
) == 2
18 assert cfg
.degree_out(n
) == 1
21 node2_props
= copy
.deepcopy(node_props
)
23 cfg
.add_node(n2
, **node2_props
)
24 cfg
.remove_edge(preds
[1], n
)
25 cfg
.add_edge(preds
[1], n2
)
26 cfg
.add_edge(n2
, cfg
.succ(n
)[0])
29 class RecursiveBlock(BBlock
):
30 """A structured block, consisting recursively of BBlock's and
33 def recursive_union(self
, func
):
35 for subb
in self
.subblocks():
40 return self
.recursive_union(lambda b
: b
.uses())
42 def defs(self
, regs_only
=True):
43 return self
.recursive_union(lambda b
: b
.defs(regs_only
))
46 class Seq(RecursiveBlock
):
47 def __init__(self
, b1
, b2
):
48 super().__init
__(b1
.addr
)
49 if isinstance(b1
, Seq
):
53 if isinstance(b2
, Seq
):
63 return "%s(%r)" % (self
.__class
__.__name
__, self
.items
)
65 def dump(self
, stream
, indent
=0, printer
=str):
67 b
.dump(stream
, indent
, printer
)
72 if cfg
.degree_out(v
) == 1:
74 if cfg
.degree_in(succ
) == 1:
75 _log
.info("seq: %s %s", v
, succ
)
76 newb
= Seq(cfg
.node(v
)["val"], cfg
.node(succ
)["val"])
77 cfg
.add_node(v
, val
=newb
)
78 cfg
.move_succ(succ
, v
)
85 if cfg
.degree_out(v
) == 2:
86 a
, b
= cfg
.sorted_succ(v
)
88 if cfg
.degree_in(a
) >= 2 and cfg
.degree_in(b
) == 1 and cfg
.degree_out(b
) == 1:
90 cond
= cfg
.edge(v
, a
).get("cond")
91 elif cfg
.degree_in(b
) >= 2 and cfg
.degree_in(a
) == 1 and cfg
.degree_out(a
) == 1:
93 cond
= cfg
.edge(v
, a
).get("cond")
100 _log
.info("if: %s, %s, %s", v
, b
, c
)
101 v
= split_bblock(cfg
, v
, ".if", only_non_empty
=True)
102 if_header
= cfg
.node(v
)["val"]
103 t_block
= cfg
.node(b
)["val"]
106 newb
= IfElse(if_header
, t_block
, None, cond
)
107 cfg
.add_node(v
, val
=newb
)
109 cfg
.set_edge(v
, a
, cond
=None)
116 class IfElse(RecursiveBlock
):
117 def __init__(self
, header
, t_block
, f_block
, true_cond
):
118 super().__init
__(header
.addr
)
120 self
.branches
= [(true_cond
, t_block
), (None, f_block
)]
123 return [x
[1] for x
in self
.branches
if x
[1]]
125 def recursive_union(self
, func
):
127 for cond
, subb
in self
.branches
:
134 def swap_branches(self
):
135 # Swap If/Else branches, negating condition
136 assert len(self
.branches
) == 2
137 assert self
.branches
[1][1] is not None
138 [(true_cond
, t_block
), (dummy
, f_block
)] = self
.branches
139 self
.branches
= [(true_cond
.neg(), f_block
), (None, t_block
)]
142 return "%s(%r)" % (self
.__class
__.__name
__, self
.branches
)
144 def dump(self
, stream
, indent
=0, printer
=str):
145 self
.write(stream
, indent
, "if %s {" % self
.branches
[0][0])
146 self
.branches
[0][1].dump(stream
, indent
+ 1, printer
)
148 for cond
, block
in self
.branches
[1:-1]:
149 self
.write(stream
, indent
, "} else if %s {" % cond
)
150 block
.dump(stream
, indent
+ 1, printer
)
152 assert self
.branches
[-1][0] is None
153 if self
.branches
[-1][1] is not None:
154 self
.write(stream
, indent
, "} else {")
155 self
.branches
[-1][1].dump(stream
, indent
+ 1, printer
)
156 self
.write(stream
, indent
, "}")
159 # if (!(a > b)) goto false
166 def match_ifelse(cfg
):
167 for v
in cfg
.nodes():
168 if cfg
.degree_out(v
) == 2:
169 succ
= cfg
.sorted_succ(v
)
170 cond
= cfg
.edge(v
, succ
[0]).get("cond")
174 f_v_s
= cfg
.succ(f_v
)
175 t_v_s
= cfg
.succ(t_v
)
177 if len(t_v_s
) != 1: continue
178 if len(f_v_s
) != 1: continue
180 common
= list(set(t_v_s
) & set(f_v_s
))
184 f_v_preds
= cfg
.pred(f_v
)
185 t_v_preds
= cfg
.pred(t_v
)
186 if len(f_v_preds
) != 1 or len(t_v_preds
) != 1:
187 _log
.warn("ifelse: %s, %s, %s, %s is part of abnormal selection", v
, t_v
, f_v
, f_v_s
[0])
192 _log
.info("ifelse: %s, %s, %s, %s", v
, t_v
, f_v
, f_v_s
[0])
193 v
= split_bblock(cfg
, v
, ".if", only_non_empty
=True)
194 if_header
= cfg
.node(v
)["val"]
195 t_block
= cfg
.node(t_v
)["val"]
196 f_block
= cfg
.node(f_v
)["val"]
197 newb
= IfElse(if_header
, t_block
, f_block
, cond
.neg())
198 cfg
.add_node(v
, val
=newb
)
201 cfg
.add_edge(v
, f_v_s
[0])
214 # It's better to transform it to:
222 # , then to be recognized by match_if_else_ladder
224 def match_if_else_inv_ladder_recursive(block
):
225 if isinstance(block
, IfElse
):
226 if len(block
.branches
) != 2:
227 _log
.warn("match_if_else_inv_ladder: Must be applied before match_if_else_ladder")
229 if_block
= block
.branches
[0][IFELSE_BRANCH
]
230 else_block
= block
.branches
[1][IFELSE_BRANCH
]
231 if isinstance(if_block
, IfElse
) and not isinstance(else_block
, IfElse
):
232 block
.swap_branches()
233 if_block
= block
.branches
[0][IFELSE_BRANCH
]
234 else_block
= block
.branches
[1][IFELSE_BRANCH
]
235 match_if_else_inv_ladder_recursive(if_block
)
236 match_if_else_inv_ladder_recursive(else_block
)
238 def match_if_else_inv_ladder(cfg
):
239 for v
, node_props
in cfg
.nodes_props():
240 block
= node_props
["val"]
241 match_if_else_inv_ladder_recursive(block
)
252 # it's equivalent to:
260 # And transforming it to such may enable match_if_else_ladder
261 def match_if_else_unjumped(cfg
):
262 for v
, node_props
in cfg
.nodes_props():
263 #print((v, node_props))
264 block
= node_props
["val"]
265 if type(block
) is BBlock
and cfg
.degree_out(v
) == 1:
266 succ
= cfg
.succ(v
)[0]
267 #print(">", (succ, cfg.node(succ)))
268 succ_block
= cfg
.node(succ
)["val"]
269 if isinstance(succ_block
, IfElse
) \
270 and succ_block
.branches
[-1][IFELSE_BRANCH
] is None \
271 and type(succ_block
.branches
[0][IFELSE_BRANCH
]) is BBlock
:
272 first_defs
= block
.defs(regs_only
=False)
273 second_defs
= succ_block
.defs(regs_only
=False)
274 second_uses
= succ_block
.uses()
275 _log
.info("ifelse_unjumped: first: defs: %s | second: defs: %s, uses: %s", first_defs
, second_defs
, second_uses
)
277 # Everything was apparently DCEed
279 if not first_defs
.issubset(second_defs
):
280 _log
.info("ifelse_unjumped: can't apply, because first defines more other vars than 2nd: %s vs %s",
281 first_defs
, second_defs
)
283 if first_defs
& second_uses
:
284 _log
.info("ifelse_unjumped: can't apply, because if uses (%s) vals defined in preceding block (%s)",
285 second_uses
, first_defs
)
287 # TODO: Are the checks above enough?
288 _log
.info("ifelse_unjumped: %s, %s", v
, succ
)
289 cfgutils
.detach_node(cfg
, v
)
290 succ_block
.branches
[-1] = (None, block
)
294 def match_if_else_ladder(cfg
):
295 for v
, node_props
in cfg
.nodes_props():
296 block
= node_props
["val"]
297 if isinstance(block
, IfElse
):
298 else_block
= block
.branches
[-1][1]
299 if isinstance(else_block
, IfElse
):
300 block
.branches
= block
.branches
[:-1] + else_block
.branches
304 class Loop(RecursiveBlock
):
305 def __init__(self
, b
):
306 super().__init
__(b
.addr
)
313 return "%s(%s)" % (self
.__class
__.__name
__, self
.items
[0])
315 def dump(self
, stream
, indent
=0, printer
=str):
316 self
.write(stream
, indent
, "while (1) {")
318 b
.dump(stream
, indent
+ 1, printer
)
319 self
.write(stream
, indent
, "}")
322 def match_infloop(cfg
):
323 for v
in cfg
.nodes():
324 if cfg
.degree_out(v
) == 1:
325 for s
in cfg
.succ(v
):
327 _log
.info("infloop: %s", v
)
328 b
= cfg
.node(v
)["val"]
330 cfg
.add_node(v
, val
=newb
)
331 cfg
.remove_edge(v
, v
)
335 class DoWhile(RecursiveBlock
):
336 def __init__(self
, b
, cond
):
337 super().__init
__(b
.addr
)
345 return "%s(%s)" % (self
.__class
__.__name
__, self
.items
[0])
347 def dump(self
, stream
, indent
=0, printer
=str):
348 self
.write(stream
, indent
, "do {")
350 b
.dump(stream
, indent
+ 1, printer
)
351 self
.write(stream
, indent
, "} while %s;" % self
.cond
)
354 def match_dowhile(cfg
):
355 for v
in cfg
.nodes():
356 if cfg
.degree_out(v
) == 2:
357 for s
in cfg
.succ(v
):
359 _log
.info("dowhile: %s", v
)
360 b
= cfg
.node(v
)["val"]
361 newb
= DoWhile(b
, cfg
.edge(v
, v
).get("cond"))
362 cfg
.add_node(v
, val
=newb
)
363 cfg
.remove_edge(v
, v
)
367 class While(RecursiveBlock
):
368 def __init__(self
, addr
, b
, cond
):
369 super().__init
__(addr
)
377 return "%s(%s)" % (self
.__class
__.__name
__, self
.items
[0])
379 def dump(self
, stream
, indent
=0, printer
=str):
380 self
.write(stream
, indent
, "while %s {" % self
.cond
)
382 b
.dump(stream
, indent
+ 1, printer
)
383 self
.write(stream
, indent
, "}")
386 def match_while(cfg
):
387 for v
in cfg
.nodes():
388 if cfg
.degree_out(v
) == 2:
389 succ
= cfg
.sorted_succ(v
)
390 back_cand
= cfg
.succ(succ
[0])
391 if len(back_cand
) == 1 and back_cand
[0] == v
and cfg
.degree_in(succ
[0]) == 1:
392 _log
.info("while: %s, %s", v
, succ
[0])
393 b
= cfg
.node(succ
[0])["val"]
394 newb
= While(v
, b
, cfg
.edge(v
, succ
[0]).get("cond"))
395 cfg
.add_node(v
, val
=newb
)
396 cfg
.remove_node(succ
[0])
401 # do {...} while (cond);
408 def match_if_dowhile(cfg
):
409 for addr
, info
in cfg
.nodes_props():
411 if type(bblock
) is IfElse
:
412 subs
= bblock
.subblocks()
413 if len(subs
) == 1 and type(subs
[0]) is DoWhile
:
414 if_cond
= bblock
.branches
[0][0]
415 dowhile_cond
= subs
[0].cond
416 #print(if_cond, if_cond == dowhile_cond)
417 if if_cond
!= dowhile_cond
:
419 while_bb
= While(bblock
.branches
[0][1], subs
[0].items
[0], if_cond
)
420 info
["val"] = while_bb
424 def match_control_and(cfg
):
425 for v
in cfg
.nodes():
426 if cfg
.degree_out(v
) == 2:
427 succ1
= cfg
.sorted_succ(v
)
429 if cfg
.degree_out(v2
) == 2:
430 succ2
= cfg
.sorted_succ(v2
)
431 assert len(succ2
) == 2
432 if succ1
[0] == succ2
[0]:
433 _log
.info("and %s, %s", v
, v2
)
436 cfg
.edge(v
, succ1
[0]).get("cond").expr
,
437 cfg
.edge(v2
, succ1
[0]).get("cond").expr
439 cfg
.edge(v
, succ1
[0]).get("cond").expr
= new_cond
441 cfg
.add_edge(v
, succ2
[1])
445 def match_abnormal_sel(cfg
):
446 """Should be run only if match_if, match_ifelse don't match anything
447 in acyclic graph. Then any join node belong to abnormal selection
448 pattern. We try to find the top-most and split it, after which normal
449 structured matches should be tried again.
451 for v
in cfg
.iter_rev_postorder():
452 if cfg
.degree_in(v
) == 2 and cfg
.degree_out(v
) == 1:
453 _log
.info("abnormal_sel: %s", v
)