-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathoptimizer.py
More file actions
218 lines (181 loc) · 6.18 KB
/
Copy pathoptimizer.py
File metadata and controls
218 lines (181 loc) · 6.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
"""
Code Optimizer
Performs optimization on Three-Address Code (TAC) generated by the ICG.
Applies multiple optimization techniques to improve efficiency and remove redundancy.
Optimizations implemented:
- Constant Folding
- Constant Propagation
- Copy Propagation
- Common Subexpression Elimination
- Dead Code Elimination
"""
from ir import Instr
#Constant folding + propagation (combined pass)
def is_const(x):
if x is None:
return False
try:
float(x)
except ValueError:
return False
return True
def parse_num(x):
if "." in x:
return float(x)
return int(x)
def format_num(value):
if isinstance(value, float) and value.is_integer():
return str(int(value))
return str(value)
def eval_binary(op, left, right):
a = parse_num(left)
b = parse_num(right)
if op == "+":
return format_num(a + b)
if op == "-":
return format_num(a - b)
if op == "*":
return format_num(a * b)
if op == "/":
if b == 0:
return None
return format_num(a / b)
if op == "%":
if b == 0:
return None
return format_num(a % b)
return None
def constant_fold_and_propagate(instrs):
const_map = {}
for instr in instrs:
if instr.label or instr.op in ["if", "goto", "print"]:
if instr.op == "if":
if instr.arg1 in const_map:
instr.arg1 = const_map[instr.arg1]
if instr.arg2 in const_map:
instr.arg2 = const_map[instr.arg2]
elif instr.op == "print":
if instr.arg1 in const_map:
instr.arg1 = const_map[instr.arg1]
if instr.op in ["if", "goto"] or instr.label:
const_map = {}
continue
if instr.arg1 in const_map:
instr.arg1 = const_map[instr.arg1]
if instr.arg2 in const_map:
instr.arg2 = const_map[instr.arg2]
if instr.op in ["+", "-", "*", "/", "%"] and is_const(instr.arg1) and is_const(instr.arg2):
folded = eval_binary(instr.op, instr.arg1, instr.arg2)
if folded is not None:
instr.op = "="
instr.arg1 = folded
instr.arg2 = None
if instr.op == "=" and instr.result and is_const(instr.arg1):
const_map[instr.result] = instr.arg1
elif instr.result:
const_map.pop(instr.result, None)
return instrs
def is_barrier(instr):
return instr.label is not None or instr.op in ["if", "goto"]
def optimize_straight_line_block(block_instrs):
changed = True
optimized = block_instrs
while changed:
before = [to_string(i) for i in optimized]
optimized = constant_fold_and_propagate(optimized)
optimized = copy_propagation(optimized)
optimized = cse(optimized)
after = [to_string(i) for i in optimized]
changed = before != after
return optimized
def optimize_by_blocks(instrs):
result = []
block = []
for instr in instrs:
if is_barrier(instr):
if block:
result.extend(optimize_straight_line_block(block))
block = []
result.append(instr)
continue
block.append(instr)
if block:
result.extend(optimize_straight_line_block(block))
return result
#Copy propagation
def copy_propagation(instrs):
copy_map = {}
for instr in instrs:
# Only substitute temporaries, never user variables
if instr.arg1 in copy_map and is_temp(instr.arg1):
instr.arg1 = copy_map[instr.arg1]
if instr.arg2 in copy_map and is_temp(instr.arg2):
instr.arg2 = copy_map[instr.arg2]
if instr.op == "=" and instr.result and instr.arg1:
# Only track temp = something or var = temp
if is_temp(instr.result) and instr.arg1.isidentifier():
copy_map[instr.result] = instr.arg1
else:
copy_map.pop(instr.result, None) # user var reassigned, invalidate
return instrs
#Common subexpression elimination
def normalize(op, a, b):
if op in ["+", "*"]:
return tuple(sorted([a, b]))
return (a, b)
def cse(instrs):
expr_map = {}
for instr in instrs:
if instr.op in ["+", "-", "*", "/", "%"]:
key = (instr.op, *normalize(instr.op, instr.arg1, instr.arg2))
if key in expr_map:
instr.op = "="
instr.arg1 = expr_map[key]
instr.arg2 = None
else:
expr_map[key] = instr.result
return instrs
def is_temp(var):
"""Temporaries start with 't' followed by digits: t0, t1, ..."""
return var is not None and len(var) > 1 and var[0] == 't' and var[1:].isdigit()
def is_variable_name(token):
return token is not None and token.isidentifier()
def dead_code(instrs):
used = set()
new_instrs = []
for instr in reversed(instrs):
keep = False
if instr.op in ["if", "goto", "print"] or instr.label:
keep = True
elif instr.result and instr.result in used:
keep = True
if keep:
new_instrs.append(instr)
if instr.result and instr.op not in ["if", "goto", "print"]:
used.discard(instr.result)
if is_variable_name(instr.arg1):
used.add(instr.arg1)
if is_variable_name(instr.arg2):
used.add(instr.arg2)
return list(reversed(new_instrs))
#Convert back to 3AC
def to_string(instr):
if instr.label:
return f"{instr.label}:"
if instr.op == "print":
return f"print {instr.arg1}"
if instr.op == "if":
return f"if {instr.arg1} {instr.relop} {instr.arg2} goto {instr.target}"
if instr.op == "goto":
return f"goto {instr.target}"
if instr.op == "=":
return f"{instr.result} = {instr.arg1}"
if instr.arg2:
return f"{instr.result} = {instr.arg1} {instr.op} {instr.arg2}"
return instr.raw
#final pipeline
def optimize(lines):
instrs = [Instr(line) for line in lines]
instrs = optimize_by_blocks(instrs)
instrs = dead_code(instrs)
return [to_string(i) for i in instrs]