Skip to content

Commit 6de27b6

Browse files
committed
Unreachable block optimization
Add optimizer for eliminating unreachable blocks.
1 parent 7e5daa0 commit 6de27b6

File tree

1 file changed

+89
-3
lines changed

1 file changed

+89
-3
lines changed

funcy/ir/optimizer.py

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,71 @@
11
from collections.abc import Callable
22

3-
from .code import Code, Op, OpType
3+
from .code import Block, Code, Op, OpType
4+
5+
def get_code_block(code: Code, label: str) -> Block:
6+
"""
7+
Get an IR block from code and a label. Return None if the block does
8+
not exist.
9+
"""
10+
11+
for block in code.blocks:
12+
if block.label == label:
13+
return block
14+
15+
return None
16+
17+
18+
def get_code_next_block(code: Code, block: Block) -> Block:
19+
"""
20+
Get the IR block from code after a block. Return None if a next
21+
block does not exist.
22+
"""
23+
24+
for i in range(len(code.blocks) - 1):
25+
if code.blocks[i] == block:
26+
return code.blocks[i + 1]
27+
28+
return None
29+
30+
31+
def is_op_label(op: Op) -> bool:
32+
"""
33+
Get whether an IR operation is a label. Label operations are
34+
operations that contain a reference to a block.
35+
"""
36+
37+
return op.type in (
38+
OpType.JUMP_LABEL, OpType.JUMP_NOT_ZERO_LABEL,
39+
OpType.JUMP_ZERO_LABEL, OpType.PUSH_LABEL)
40+
441

542
def is_op_terminator(op: Op) -> bool:
643
"""
744
Get whether an IR operation is a terminator. Terminator operations
845
are operations that guarantee that any subsequent operations will
946
not be executed.
1047
"""
48+
1149
return op.type in (OpType.HALT, OpType.JUMP_LABEL, OpType.RETURN)
1250

1351

52+
def is_block_terminated(block: Block) -> bool:
53+
"""
54+
Get whether an IR block is terminated. Terminated blocks are blocks
55+
that contain a terminator operation.
56+
"""
57+
58+
for op in block.ops:
59+
if is_op_terminator(op):
60+
return True
61+
62+
return False
63+
64+
1465
def optimizer_eliminate_unreachable_ops(code: Code) -> bool:
1566
"""
16-
Eliminates unreachable IR operations that follow a terminator
17-
operation. Returns whether any optimization was performed.
67+
Eliminate unreachable IR operations that follow a terminator
68+
operation. Return whether any optimization was performed.
1869
"""
1970

2071
was_optimized: bool = False
@@ -29,11 +80,46 @@ def optimizer_eliminate_unreachable_ops(code: Code) -> bool:
2980
return was_optimized
3081

3182

83+
def optimizer_eliminate_unreachable_blocks(code: Code) -> bool:
84+
"""
85+
Eliminate unreachable IR blocks that are never referenced by a label
86+
operation or preceded by an unterminated block. Return whether any
87+
optimization was performed.
88+
"""
89+
90+
was_optimized: bool = False
91+
pending_blocks: list[Block] = [get_code_block(code, ".main")]
92+
reachable_blocks: list[Block] = []
93+
94+
while pending_blocks:
95+
block: Block = pending_blocks.pop()
96+
97+
if block is None or block in reachable_blocks:
98+
continue
99+
100+
reachable_blocks.append(block)
101+
102+
for op in block.ops:
103+
if is_op_label(op):
104+
pending_blocks.append(get_code_block(code, op.str_value))
105+
106+
if not is_block_terminated(block):
107+
pending_blocks.append(get_code_next_block(code, block))
108+
109+
for i in range(len(code.blocks) - 1, -1, -1):
110+
if not code.blocks[i] in reachable_blocks:
111+
code.blocks.pop(i)
112+
was_optimized = True
113+
114+
return was_optimized
115+
116+
32117
def optimize_code(code: Code) -> None:
33118
""" Optimize an IR code program. """
34119

35120
OPTIMIZERS: list[Callable[[Code], bool]] = [
36121
optimizer_eliminate_unreachable_ops,
122+
optimizer_eliminate_unreachable_blocks,
37123
]
38124

39125
should_optimize: bool = True

0 commit comments

Comments
 (0)