angr 9.2.97__py3-none-win_amd64.whl → 9.2.99__py3-none-win_amd64.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.

Files changed (33) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/cfg/cfg_base.py +14 -1
  3. angr/analyses/cfg/cfg_fast.py +3 -3
  4. angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +10 -6
  5. angr/analyses/decompiler/clinic.py +2 -40
  6. angr/analyses/decompiler/optimization_passes/__init__.py +2 -0
  7. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +380 -0
  8. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +10 -2
  9. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +4 -1
  10. angr/analyses/decompiler/peephole_optimizations/__init__.py +1 -0
  11. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +71 -3
  12. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +162 -0
  13. angr/analyses/decompiler/region_simplifiers/expr_folding.py +5 -3
  14. angr/analyses/decompiler/return_maker.py +71 -0
  15. angr/analyses/decompiler/structured_codegen/__init__.py +1 -1
  16. angr/analyses/decompiler/structured_codegen/c.py +72 -99
  17. angr/analyses/decompiler/utils.py +5 -1
  18. angr/analyses/propagator/engine_vex.py +15 -0
  19. angr/analyses/reaching_definitions/engine_vex.py +6 -0
  20. angr/analyses/variable_recovery/engine_vex.py +6 -0
  21. angr/analyses/variable_recovery/irsb_scanner.py +12 -0
  22. angr/engines/light/engine.py +126 -15
  23. angr/knowledge_plugins/functions/function.py +4 -0
  24. angr/lib/angr_native.dll +0 -0
  25. angr/storage/memory_mixins/paged_memory/pages/list_page.py +20 -5
  26. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +2 -1
  27. angr/storage/memory_mixins/simple_interface_mixin.py +4 -0
  28. {angr-9.2.97.dist-info → angr-9.2.99.dist-info}/METADATA +6 -6
  29. {angr-9.2.97.dist-info → angr-9.2.99.dist-info}/RECORD +33 -30
  30. {angr-9.2.97.dist-info → angr-9.2.99.dist-info}/LICENSE +0 -0
  31. {angr-9.2.97.dist-info → angr-9.2.99.dist-info}/WHEEL +0 -0
  32. {angr-9.2.97.dist-info → angr-9.2.99.dist-info}/entry_points.txt +0 -0
  33. {angr-9.2.97.dist-info → angr-9.2.99.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  # pylint:disable=arguments-differ
2
- from typing import Tuple, Optional
2
+ from typing import Tuple, Optional, Dict, List
3
3
  import string
4
4
 
5
5
  from archinfo import Endness
6
6
 
7
- from ailment.expression import Const
7
+ from ailment.expression import Const, StackBaseOffset
8
8
  from ailment.statement import Call, Store
9
9
 
10
10
  from .base import PeepholeOptimizationStmtBase
@@ -24,7 +24,7 @@ class InlinedStrcpy(PeepholeOptimizationStmtBase):
24
24
  NAME = "Simplifying inlined strcpy"
25
25
  stmt_classes = (Store,)
26
26
 
27
- def optimize(self, stmt: Store, **kwargs):
27
+ def optimize(self, stmt: Store, stmt_idx: int = None, block=None, **kwargs):
28
28
  if isinstance(stmt.data, Const):
29
29
  r, s = self.is_integer_likely_a_string(stmt.data.value, stmt.data.size, stmt.endness)
30
30
  if r:
@@ -41,8 +41,76 @@ class InlinedStrcpy(PeepholeOptimizationStmtBase):
41
41
  **stmt.tags,
42
42
  )
43
43
 
44
+ # scan forward in the current block to find all consecutive constant stores
45
+ if block is not None and stmt_idx is not None:
46
+ all_constant_stores: Dict[int, Tuple[int, Optional[Const]]] = self.collect_constant_stores(
47
+ block, stmt_idx
48
+ )
49
+ if all_constant_stores:
50
+ offsets = sorted(all_constant_stores.keys())
51
+ next_offset = min(offsets)
52
+ stride = []
53
+ for offset in offsets:
54
+ if next_offset is not None and offset != next_offset:
55
+ next_offset = None
56
+ stride = []
57
+ stmt_idx_, v = all_constant_stores[offset]
58
+ if v is not None:
59
+ stride.append((offset, stmt_idx_, v))
60
+ next_offset = offset + v.size
61
+ else:
62
+ next_offset = None
63
+ stride = []
64
+
65
+ integer, size = self.stride_to_int(stride)
66
+ r, s = self.is_integer_likely_a_string(integer, size, Endness.BE)
67
+ if r:
68
+ # we remove all involved statements whose statement IDs are greater than the current one
69
+ for _, stmt_idx_, _ in reversed(stride):
70
+ if stmt_idx_ <= stmt_idx:
71
+ continue
72
+ block.statements[stmt_idx_] = None
73
+ block.statements = [ss for ss in block.statements if ss is not None]
74
+
75
+ str_id = self.kb.custom_strings.allocate(s.encode("ascii"))
76
+ return Call(
77
+ stmt.idx,
78
+ "strncpy",
79
+ args=[
80
+ stmt.addr,
81
+ Const(None, None, str_id, stmt.addr.bits, custom_string=True),
82
+ Const(None, None, len(s), self.project.arch.bits),
83
+ ],
84
+ **stmt.tags,
85
+ )
86
+
44
87
  return None
45
88
 
89
+ @staticmethod
90
+ def stride_to_int(stride: List[Tuple[int, int, Const]]) -> Tuple[int, int]:
91
+ stride = sorted(stride, key=lambda x: x[0])
92
+ n = 0
93
+ size = 0
94
+ for _, _, v in stride:
95
+ size += v.size
96
+ n <<= v.bits
97
+ n |= v.value
98
+ return n, size
99
+
100
+ @staticmethod
101
+ def collect_constant_stores(block, starting_stmt_idx: int) -> Dict[int, Tuple[int, Optional[Const]]]:
102
+ r = {}
103
+ for idx, stmt in enumerate(block.statements):
104
+ if idx < starting_stmt_idx:
105
+ continue
106
+ if isinstance(stmt, Store) and isinstance(stmt.addr, StackBaseOffset) and isinstance(stmt.addr.offset, int):
107
+ if isinstance(stmt.data, Const):
108
+ r[stmt.addr.offset] = idx, stmt.data
109
+ else:
110
+ r[stmt.addr.offset] = idx, None
111
+
112
+ return r
113
+
46
114
  @staticmethod
47
115
  def is_integer_likely_a_string(
48
116
  v: int, size: int, endness: Endness, min_length: int = 4
@@ -0,0 +1,162 @@
1
+ # pylint:disable=arguments-differ
2
+ from typing import Tuple, Optional, Dict, List
3
+ import string
4
+
5
+ from archinfo import Endness
6
+
7
+ from ailment.expression import Const, StackBaseOffset
8
+ from ailment.statement import Call, Store
9
+
10
+ from .base import PeepholeOptimizationStmtBase
11
+
12
+
13
+ ASCII_PRINTABLES = set(string.printable)
14
+ ASCII_DIGITS = set(string.digits)
15
+
16
+
17
+ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
18
+ """
19
+ Simplifies inlined wide string copying logic into calls to wstrcpy.
20
+ """
21
+
22
+ __slots__ = ()
23
+
24
+ NAME = "Simplifying inlined wstrcpy"
25
+ stmt_classes = (Store,)
26
+
27
+ def optimize(self, stmt: Store, stmt_idx: int = None, block=None, **kwargs):
28
+ if isinstance(stmt.data, Const):
29
+ r, s = self.is_integer_likely_a_wide_string(stmt.data.value, stmt.data.size, stmt.endness)
30
+ if r:
31
+ # replace it with a call to strncpy
32
+ str_id = self.kb.custom_strings.allocate(s.encode("ascii"))
33
+ return Call(
34
+ stmt.idx,
35
+ "wstrncpy",
36
+ args=[
37
+ stmt.addr,
38
+ Const(None, None, str_id, stmt.addr.bits, custom_string=True),
39
+ Const(None, None, len(s), self.project.arch.bits),
40
+ ],
41
+ **stmt.tags,
42
+ )
43
+
44
+ # scan forward in the current block to find all consecutive constant stores
45
+ if block is not None and stmt_idx is not None:
46
+ all_constant_stores: Dict[int, Tuple[int, Optional[Const]]] = self.collect_constant_stores(
47
+ block, stmt_idx
48
+ )
49
+ if all_constant_stores:
50
+ offsets = sorted(all_constant_stores.keys())
51
+ next_offset = min(offsets)
52
+ stride = []
53
+ for offset in offsets:
54
+ if next_offset is not None and offset != next_offset:
55
+ next_offset = None
56
+ stride = []
57
+ stmt_idx_, v = all_constant_stores[offset]
58
+ if v is not None:
59
+ stride.append((offset, stmt_idx_, v))
60
+ next_offset = offset + v.size
61
+ else:
62
+ next_offset = None
63
+ stride = []
64
+
65
+ integer, size = self.stride_to_int(stride)
66
+ r, s = self.is_integer_likely_a_wide_string(integer, size, Endness.BE)
67
+ if r:
68
+ # we remove all involved statements whose statement IDs are greater than the current one
69
+ for _, stmt_idx_, _ in reversed(stride):
70
+ if stmt_idx_ <= stmt_idx:
71
+ continue
72
+ block.statements[stmt_idx_] = None
73
+ block.statements = [ss for ss in block.statements if ss is not None]
74
+
75
+ str_id = self.kb.custom_strings.allocate(s.encode("ascii"))
76
+ return Call(
77
+ stmt.idx,
78
+ "wstrncpy",
79
+ args=[
80
+ stmt.addr,
81
+ Const(None, None, str_id, stmt.addr.bits, custom_string=True),
82
+ Const(None, None, len(s), self.project.arch.bits),
83
+ ],
84
+ **stmt.tags,
85
+ )
86
+
87
+ return None
88
+
89
+ @staticmethod
90
+ def stride_to_int(stride: List[Tuple[int, int, Const]]) -> Tuple[int, int]:
91
+ stride = sorted(stride, key=lambda x: x[0])
92
+ n = 0
93
+ size = 0
94
+ for _, _, v in stride:
95
+ size += v.size
96
+ n <<= v.bits
97
+ n |= v.value
98
+ return n, size
99
+
100
+ @staticmethod
101
+ def collect_constant_stores(block, starting_stmt_idx: int) -> Dict[int, Tuple[int, Optional[Const]]]:
102
+ r = {}
103
+ for idx, stmt in enumerate(block.statements):
104
+ if idx < starting_stmt_idx:
105
+ continue
106
+ if isinstance(stmt, Store) and isinstance(stmt.addr, StackBaseOffset) and isinstance(stmt.addr.offset, int):
107
+ if isinstance(stmt.data, Const):
108
+ r[stmt.addr.offset] = idx, stmt.data
109
+ else:
110
+ r[stmt.addr.offset] = idx, None
111
+
112
+ return r
113
+
114
+ @staticmethod
115
+ def even_offsets_are_zero(lst: List[str]) -> bool:
116
+ return all(ch == "\x00" for i, ch in enumerate(lst) if i % 2 == 0)
117
+
118
+ @staticmethod
119
+ def odd_offsets_are_zero(lst: List[str]) -> bool:
120
+ return all(ch == "\x00" for i, ch in enumerate(lst) if i % 2 == 1)
121
+
122
+ @staticmethod
123
+ def is_integer_likely_a_wide_string(
124
+ v: int, size: int, endness: Endness, min_length: int = 4
125
+ ) -> Tuple[bool, Optional[str]]:
126
+ # we need at least four bytes of printable characters
127
+
128
+ chars = []
129
+ if endness == Endness.LE:
130
+ while v != 0:
131
+ byt = v & 0xFF
132
+ if byt != 0 and chr(byt) not in ASCII_PRINTABLES:
133
+ return False, None
134
+ chars.append(chr(byt))
135
+ v >>= 8
136
+
137
+ elif endness == Endness.BE:
138
+ for _ in range(size):
139
+ byt = v & 0xFF
140
+ v >>= 8
141
+ if byt != 0 and chr(byt) not in ASCII_PRINTABLES:
142
+ return False, None
143
+ chars.append(chr(byt))
144
+ chars = chars[::-1]
145
+ else:
146
+ # unsupported endness
147
+ return False, None
148
+
149
+ if InlinedWstrcpy.even_offsets_are_zero(chars):
150
+ chars = [ch for i, ch in enumerate(chars) if i % 2 == 1]
151
+ elif InlinedWstrcpy.odd_offsets_are_zero(chars):
152
+ chars = [ch for i, ch in enumerate(chars) if i % 2 == 0]
153
+ else:
154
+ return False, None
155
+
156
+ if chars and chars[-1] == "\x00":
157
+ chars = chars[:-1]
158
+ if len(chars) >= min_length and all(ch in ASCII_PRINTABLES for ch in chars):
159
+ if len(chars) <= 4 and all(ch in ASCII_DIGITS for ch in chars):
160
+ return False, None
161
+ return True, "".join(chars)
162
+ return False, None
@@ -4,6 +4,7 @@ from typing import Optional, Any, Dict, Set, Tuple, Iterable, Union, DefaultDict
4
4
 
5
5
  import ailment
6
6
  from ailment import Expression, Block, AILBlockWalker
7
+ from ailment.expression import ITE
7
8
  from ailment.statement import Statement, Assignment, Call
8
9
 
9
10
  from ..sequence_walker import SequenceWalker
@@ -385,11 +386,11 @@ class ExpressionReplacer(AILBlockWalker):
385
386
  return None
386
387
 
387
388
  def _handle_Assignment(self, stmt_idx: int, stmt: Assignment, block: Optional[Block]):
388
- # override the base handler and make sure we do not replace .dst with a Call expression
389
+ # override the base handler and make sure we do not replace .dst with a Call expression or an ITE expression
389
390
  changed = False
390
391
 
391
392
  dst = self._handle_expr(0, stmt.dst, stmt_idx, stmt, block)
392
- if dst is not None and dst is not stmt.dst and not isinstance(dst, Call):
393
+ if dst is not None and dst is not stmt.dst and not isinstance(dst, (Call, ITE)):
393
394
  changed = True
394
395
  else:
395
396
  dst = stmt.dst
@@ -446,7 +447,8 @@ class ExpressionFolder(SequenceWalker):
446
447
  for stmt in node.statements:
447
448
  if isinstance(stmt, ailment.Stmt.Assignment):
448
449
  if isinstance(stmt.dst, ailment.Expr.Register) and stmt.dst.variable is not None:
449
- if stmt.dst.variable in self._assignments:
450
+ unified_var = self._u(stmt.dst.variable)
451
+ if unified_var in self._assignments:
450
452
  # remove this statement
451
453
  continue
452
454
  if (
@@ -0,0 +1,71 @@
1
+ from typing import Optional
2
+ import logging
3
+
4
+ import ailment
5
+
6
+ from angr.sim_type import SimTypeBottom
7
+ from angr.calling_conventions import SimRegArg
8
+ from .ailgraph_walker import AILGraphWalker
9
+
10
+ l = logging.getLogger(__name__)
11
+
12
+
13
+ class ReturnMaker(AILGraphWalker):
14
+ """
15
+ Traverse the AILBlock graph of a function and update .ret_exprs of all return statements.
16
+ """
17
+
18
+ def __init__(self, ail_manager, arch, function, ail_graph):
19
+ super().__init__(ail_graph, self._handler, replace_nodes=True)
20
+ self.ail_manager = ail_manager
21
+ self.arch = arch
22
+ self.function = function
23
+ self._new_block = None
24
+
25
+ self.walk()
26
+
27
+ def _next_atom(self) -> int:
28
+ return self.ail_manager.next_atom()
29
+
30
+ def _handle_Return(
31
+ self, stmt_idx: int, stmt: ailment.Stmt.Return, block: Optional[ailment.Block]
32
+ ): # pylint:disable=unused-argument
33
+ if (
34
+ block is not None
35
+ and not stmt.ret_exprs
36
+ and self.function.prototype is not None
37
+ and self.function.prototype.returnty is not None
38
+ and type(self.function.prototype.returnty) is not SimTypeBottom
39
+ ):
40
+ new_stmt = stmt.copy()
41
+ ret_val = self.function.calling_convention.return_val(self.function.prototype.returnty)
42
+ if isinstance(ret_val, SimRegArg):
43
+ reg = self.arch.registers[ret_val.reg_name]
44
+ new_stmt.ret_exprs.append(
45
+ ailment.Expr.Register(
46
+ self._next_atom(),
47
+ None,
48
+ reg[0],
49
+ ret_val.size * self.arch.byte_width,
50
+ reg_name=self.arch.translate_register_name(reg[0], ret_val.size),
51
+ )
52
+ )
53
+ else:
54
+ l.warning("Unsupported type of return expression %s.", type(ret_val))
55
+ new_statements = block.statements[::]
56
+ new_statements[stmt_idx] = new_stmt
57
+ self._new_block = block.copy(statements=new_statements)
58
+
59
+ def _handler(self, block):
60
+ walker = ailment.AILBlockWalker()
61
+ # we don't need to handle any statement besides Returns
62
+ walker.stmt_handlers.clear()
63
+ walker.expr_handlers.clear()
64
+ walker.stmt_handlers[ailment.Stmt.Return] = self._handle_Return
65
+
66
+ self._new_block = None
67
+ walker.walk(block)
68
+
69
+ if self._new_block is not None:
70
+ return self._new_block
71
+ return None
@@ -5,6 +5,6 @@ from .base import (
5
5
  PositionMappingElement,
6
6
  PositionMapping,
7
7
  )
8
- from .c import CStructuredCodeGenerator
8
+ from .c import CStructuredCodeGenerator, CStructuredCodeWalker
9
9
  from .dwarf_import import ImportSourceCode
10
10
  from .dummy import DummyStructuredCodeGenerator
@@ -1,4 +1,4 @@
1
- # pylint:disable=missing-class-docstring,too-many-boolean-expressions,unused-argument
1
+ # pylint:disable=missing-class-docstring,too-many-boolean-expressions,unused-argument,no-self-use
2
2
  from typing import Optional, Dict, List, Tuple, Set, Any, Union, TYPE_CHECKING, Callable
3
3
  from collections import defaultdict
4
4
  import logging
@@ -2524,9 +2524,9 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
2524
2524
  codegen=self,
2525
2525
  omit_header=self.omit_func_header,
2526
2526
  )
2527
- self.cfunc = FieldReferenceCleanup.handle(self.cfunc)
2528
- self.cfunc = PointerArithmeticFixer.handle(self.cfunc)
2529
- self.cfunc = MakeTypecastsImplicit.handle(self.cfunc)
2527
+ self.cfunc = FieldReferenceCleanup().handle(self.cfunc)
2528
+ self.cfunc = PointerArithmeticFixer().handle(self.cfunc)
2529
+ self.cfunc = MakeTypecastsImplicit().handle(self.cfunc)
2530
2530
 
2531
2531
  # TODO store extern fallback size somewhere lol
2532
2532
  self.cexterns = {
@@ -3554,120 +3554,100 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3554
3554
 
3555
3555
 
3556
3556
  class CStructuredCodeWalker:
3557
- @classmethod
3558
- def handle(cls, obj):
3559
- handler = getattr(cls, "handle_" + type(obj).__name__, cls.handle_default)
3557
+ def handle(self, obj):
3558
+ handler = getattr(self, "handle_" + type(obj).__name__, self.handle_default)
3560
3559
  return handler(obj)
3561
3560
 
3562
- @classmethod
3563
- def handle_default(cls, obj):
3561
+ def handle_default(self, obj):
3564
3562
  return obj
3565
3563
 
3566
- @classmethod
3567
- def handle_CFunction(cls, obj):
3568
- obj.statements = cls.handle(obj.statements)
3564
+ def handle_CFunction(self, obj):
3565
+ obj.statements = self.handle(obj.statements)
3569
3566
  return obj
3570
3567
 
3571
- @classmethod
3572
- def handle_CStatements(cls, obj):
3573
- obj.statements = [cls.handle(stmt) for stmt in obj.statements]
3568
+ def handle_CStatements(self, obj):
3569
+ obj.statements = [self.handle(stmt) for stmt in obj.statements]
3574
3570
  return obj
3575
3571
 
3576
- @classmethod
3577
- def handle_CWhileLoop(cls, obj):
3578
- obj.condition = cls.handle(obj.condition)
3579
- obj.body = cls.handle(obj.body)
3572
+ def handle_CWhileLoop(self, obj):
3573
+ obj.condition = self.handle(obj.condition)
3574
+ obj.body = self.handle(obj.body)
3580
3575
  return obj
3581
3576
 
3582
- @classmethod
3583
- def handle_CDoWhileLoop(cls, obj):
3584
- obj.condition = cls.handle(obj.condition)
3585
- obj.body = cls.handle(obj.body)
3577
+ def handle_CDoWhileLoop(self, obj):
3578
+ obj.condition = self.handle(obj.condition)
3579
+ obj.body = self.handle(obj.body)
3586
3580
  return obj
3587
3581
 
3588
- @classmethod
3589
- def handle_CForLoop(cls, obj):
3590
- obj.initializer = cls.handle(obj.initializer)
3591
- obj.condition = cls.handle(obj.condition)
3592
- obj.iterator = cls.handle(obj.iterator)
3593
- obj.body = cls.handle(obj.body)
3582
+ def handle_CForLoop(self, obj):
3583
+ obj.initializer = self.handle(obj.initializer)
3584
+ obj.condition = self.handle(obj.condition)
3585
+ obj.iterator = self.handle(obj.iterator)
3586
+ obj.body = self.handle(obj.body)
3594
3587
  return obj
3595
3588
 
3596
- @classmethod
3597
- def handle_CIfElse(cls, obj):
3589
+ def handle_CIfElse(self, obj):
3598
3590
  obj.condition_and_nodes = [
3599
- (cls.handle(condition), cls.handle(node)) for condition, node in obj.condition_and_nodes
3591
+ (self.handle(condition), self.handle(node)) for condition, node in obj.condition_and_nodes
3600
3592
  ]
3601
- obj.else_node = cls.handle(obj.else_node)
3593
+ obj.else_node = self.handle(obj.else_node)
3602
3594
  return obj
3603
3595
 
3604
- @classmethod
3605
- def handle_CIfBreak(cls, obj):
3606
- obj.condition = cls.handle(obj.condition)
3596
+ def handle_CIfBreak(self, obj):
3597
+ obj.condition = self.handle(obj.condition)
3607
3598
  return obj
3608
3599
 
3609
- @classmethod
3610
- def handle_CSwitchCase(cls, obj):
3611
- obj.switch = cls.handle(obj.switch)
3612
- obj.cases = [(case, cls.handle(body)) for case, body in obj.cases]
3613
- obj.default = cls.handle(obj.default)
3600
+ def handle_CSwitchCase(self, obj):
3601
+ obj.switch = self.handle(obj.switch)
3602
+ obj.cases = [(case, self.handle(body)) for case, body in obj.cases]
3603
+ obj.default = self.handle(obj.default)
3614
3604
  return obj
3615
3605
 
3616
- @classmethod
3617
- def handle_CAssignment(cls, obj):
3618
- obj.lhs = cls.handle(obj.lhs)
3619
- obj.rhs = cls.handle(obj.rhs)
3606
+ def handle_CAssignment(self, obj):
3607
+ obj.lhs = self.handle(obj.lhs)
3608
+ obj.rhs = self.handle(obj.rhs)
3620
3609
  return obj
3621
3610
 
3622
- @classmethod
3623
- def handle_CFunctionCall(cls, obj):
3624
- obj.callee_target = cls.handle(obj.callee_target)
3625
- obj.args = [cls.handle(arg) for arg in obj.args]
3626
- obj.ret_expr = cls.handle(obj.ret_expr)
3611
+ def handle_CFunctionCall(self, obj):
3612
+ obj.callee_target = self.handle(obj.callee_target)
3613
+ obj.args = [self.handle(arg) for arg in obj.args]
3614
+ obj.ret_expr = self.handle(obj.ret_expr)
3627
3615
  return obj
3628
3616
 
3629
- @classmethod
3630
- def handle_CReturn(cls, obj):
3631
- obj.retval = cls.handle(obj.retval)
3617
+ def handle_CReturn(self, obj):
3618
+ obj.retval = self.handle(obj.retval)
3632
3619
  return obj
3633
3620
 
3634
- @classmethod
3635
- def handle_CGoto(cls, obj):
3636
- obj.target = cls.handle(obj.target)
3621
+ def handle_CGoto(self, obj):
3622
+ obj.target = self.handle(obj.target)
3637
3623
  return obj
3638
3624
 
3639
- @classmethod
3640
- def handle_CIndexedVariable(cls, obj):
3641
- obj.variable = cls.handle(obj.variable)
3642
- obj.index = cls.handle(obj.index)
3625
+ def handle_CIndexedVariable(self, obj):
3626
+ obj.variable = self.handle(obj.variable)
3627
+ obj.index = self.handle(obj.index)
3643
3628
  return obj
3644
3629
 
3645
- @classmethod
3646
- def handle_CVariableField(cls, obj):
3647
- obj.variable = cls.handle(obj.variable)
3630
+ def handle_CVariableField(self, obj):
3631
+ obj.variable = self.handle(obj.variable)
3648
3632
  return obj
3649
3633
 
3650
- @classmethod
3651
- def handle_CUnaryOp(cls, obj):
3652
- obj.operand = cls.handle(obj.operand)
3634
+ def handle_CUnaryOp(self, obj):
3635
+ obj.operand = self.handle(obj.operand)
3653
3636
  return obj
3654
3637
 
3655
- @classmethod
3656
- def handle_CBinaryOp(cls, obj):
3657
- obj.lhs = cls.handle(obj.lhs)
3658
- obj.rhs = cls.handle(obj.rhs)
3638
+ def handle_CBinaryOp(self, obj):
3639
+ obj.lhs = self.handle(obj.lhs)
3640
+ obj.rhs = self.handle(obj.rhs)
3659
3641
  return obj
3660
3642
 
3661
- @classmethod
3662
- def handle_CTypeCast(cls, obj):
3663
- obj.expr = cls.handle(obj.expr)
3643
+ def handle_CTypeCast(self, obj):
3644
+ obj.expr = self.handle(obj.expr)
3664
3645
  return obj
3665
3646
 
3666
- @classmethod
3667
- def handle_CITE(cls, obj):
3668
- obj.cond = cls.handle(obj.cond)
3669
- obj.iftrue = cls.handle(obj.iftrue)
3670
- obj.iffalse = cls.handle(obj.iffalse)
3647
+ def handle_CITE(self, obj):
3648
+ obj.cond = self.handle(obj.cond)
3649
+ obj.iftrue = self.handle(obj.iftrue)
3650
+ obj.iffalse = self.handle(obj.iffalse)
3671
3651
  return obj
3672
3652
 
3673
3653
 
@@ -3702,34 +3682,30 @@ class MakeTypecastsImplicit(CStructuredCodeWalker):
3702
3682
  return cls.collapse(dst_ty, result)
3703
3683
  return result
3704
3684
 
3705
- @classmethod
3706
- def handle_CAssignment(cls, obj):
3707
- obj.rhs = cls.collapse(obj.lhs.type, obj.rhs)
3685
+ def handle_CAssignment(self, obj):
3686
+ obj.rhs = self.collapse(obj.lhs.type, obj.rhs)
3708
3687
  return super().handle_CAssignment(obj)
3709
3688
 
3710
- @classmethod
3711
- def handle_CFunctionCall(cls, obj: CFunctionCall):
3689
+ def handle_CFunctionCall(self, obj: CFunctionCall):
3712
3690
  for i, (c_arg, arg_ty) in enumerate(zip(obj.args, obj.prototype.args)):
3713
- obj.args[i] = cls.collapse(arg_ty, c_arg)
3691
+ obj.args[i] = self.collapse(arg_ty, c_arg)
3714
3692
  return super().handle_CFunctionCall(obj)
3715
3693
 
3716
- @classmethod
3717
- def handle_CReturn(cls, obj: CReturn):
3718
- obj.retval = cls.collapse(obj.codegen._func.prototype.returnty, obj.retval)
3694
+ def handle_CReturn(self, obj: CReturn):
3695
+ obj.retval = self.collapse(obj.codegen._func.prototype.returnty, obj.retval)
3719
3696
  return super().handle_CReturn(obj)
3720
3697
 
3721
- @classmethod
3722
- def handle_CBinaryOp(cls, obj: CBinaryOp):
3698
+ def handle_CBinaryOp(self, obj: CBinaryOp):
3723
3699
  obj = super().handle_CBinaryOp(obj)
3724
3700
  while True:
3725
- new_lhs = cls.collapse(obj.common_type, obj.lhs)
3701
+ new_lhs = self.collapse(obj.common_type, obj.lhs)
3726
3702
  if (
3727
3703
  new_lhs is not obj.lhs
3728
3704
  and CBinaryOp.compute_common_type(obj.op, new_lhs.type, obj.rhs.type) == obj.common_type
3729
3705
  ):
3730
3706
  obj.lhs = new_lhs
3731
3707
  else:
3732
- new_rhs = cls.collapse(obj.common_type, obj.rhs)
3708
+ new_rhs = self.collapse(obj.common_type, obj.rhs)
3733
3709
  if (
3734
3710
  new_rhs is not obj.rhs
3735
3711
  and CBinaryOp.compute_common_type(obj.op, obj.lhs.type, new_rhs.type) == obj.common_type
@@ -3739,11 +3715,10 @@ class MakeTypecastsImplicit(CStructuredCodeWalker):
3739
3715
  break
3740
3716
  return obj
3741
3717
 
3742
- @classmethod
3743
- def handle_CTypeCast(cls, obj: CTypeCast):
3718
+ def handle_CTypeCast(self, obj: CTypeCast):
3744
3719
  # note that the expression that this method returns may no longer be a CTypeCast
3745
3720
  obj = super().handle_CTypeCast(obj)
3746
- inner = cls.collapse(obj.dst_type, obj.expr)
3721
+ inner = self.collapse(obj.dst_type, obj.expr)
3747
3722
  if inner is not obj.expr:
3748
3723
  obj.src_type = inner.type
3749
3724
  obj.expr = inner
@@ -3753,12 +3728,11 @@ class MakeTypecastsImplicit(CStructuredCodeWalker):
3753
3728
 
3754
3729
 
3755
3730
  class FieldReferenceCleanup(CStructuredCodeWalker):
3756
- @classmethod
3757
- def handle_CTypeCast(cls, obj):
3731
+ def handle_CTypeCast(self, obj):
3758
3732
  if isinstance(obj.dst_type, SimTypePointer) and not isinstance(obj.dst_type.pts_to, SimTypeBottom):
3759
3733
  obj = obj.codegen._access_reference(obj.expr, obj.dst_type.pts_to)
3760
3734
  if not isinstance(obj, CTypeCast):
3761
- return cls.handle(obj)
3735
+ return self.handle(obj)
3762
3736
  return super().handle_CTypeCast(obj)
3763
3737
 
3764
3738
 
@@ -3776,8 +3750,7 @@ class PointerArithmeticFixer(CStructuredCodeWalker):
3776
3750
  a_ptr = a_ptr + 1.
3777
3751
  """
3778
3752
 
3779
- @classmethod
3780
- def handle_CBinaryOp(cls, obj):
3753
+ def handle_CBinaryOp(self, obj):
3781
3754
  obj: CBinaryOp = super().handle_CBinaryOp(obj)
3782
3755
  if (
3783
3756
  obj.op in ("Add", "Sub")
@@ -569,7 +569,10 @@ def peephole_optimize_stmts(block, stmt_opts):
569
569
  statements = []
570
570
 
571
571
  # run statement optimizers
572
- for stmt_idx, stmt in enumerate(block.statements):
572
+ # note that an optimizer may optionally edit or remove statements whose statement IDs are greater than stmt_idx
573
+ stmt_idx = 0
574
+ while stmt_idx < len(block.statements):
575
+ stmt = block.statements[stmt_idx]
573
576
  old_stmt = stmt
574
577
  redo = True
575
578
  while redo:
@@ -587,6 +590,7 @@ def peephole_optimize_stmts(block, stmt_opts):
587
590
  any_update = True
588
591
  else:
589
592
  statements.append(old_stmt)
593
+ stmt_idx += 1
590
594
 
591
595
  return statements, any_update
592
596