Skip to content

Commit b6f1245

Browse files
committed
Add a very primitive/basic peephole optimizer. This isn't likely to
stay in the long run, but it papers over some of the awfulness of the current codegen quality until more serious overhauls are done.
1 parent 6d60605 commit b6f1245

File tree

2 files changed

+279
-1
lines changed

2 files changed

+279
-1
lines changed

emitter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def int_value(param)
2121

2222
require 'iooutput'
2323
require 'arrayoutput'
24+
require 'peephole'
2425

2526
# Emitter class.
2627
# Emits assembly code for x86 (32 Bit) architecture.
@@ -37,7 +38,7 @@ class Emitter
3738

3839
def initialize out = IOOutput.new
3940
@seq = 0
40-
@out = out
41+
@out = Peephole.new(out)
4142
@basic_main = false
4243
@section = 0 # Are we in a stabs section?
4344
@allocator = RegisterAllocator.new

peephole.rb

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
2+
# # Very basic peephole optimizer
3+
#
4+
# This is *not* intended to be a general purpose peephole optimizer
5+
# It's a quick and dirty hack to encapsulate optimizations that can
6+
# be made quickly on the asm that is harder to fix in the higher level
7+
# code generation. It is allowed to make assumptions about the generated
8+
# code which may not hold for asm generated by anything else.
9+
#
10+
# This is i386 specific at the moment
11+
12+
13+
class Peephole
14+
def initialize out
15+
@out = out
16+
@prev = []
17+
end
18+
19+
def comment(str)
20+
#flush
21+
#@out.comment(str)
22+
end
23+
24+
def export(label, type = nil)
25+
flush
26+
@out.export(label, type)
27+
end
28+
29+
def label(l)
30+
flush
31+
@out.label(l)
32+
end
33+
34+
def match(pat, test)
35+
return nil if pat.length != test.length
36+
res = []
37+
pat.each_with_index do |c,i|
38+
if c === test[i]
39+
res << test[i]
40+
else
41+
return nil
42+
end
43+
end
44+
return res
45+
end
46+
47+
# Other patterns:
48+
# pushl %eax
49+
# movl ?, %eax
50+
# movl %eax, otherreg
51+
# popl %eax
52+
# ->
53+
# movl ?, reg
54+
55+
# subl n, %esp
56+
# ... x ops not touching %esp
57+
# addl n, %esp
58+
# ->
59+
# ... x ops not touching %esp
60+
# *especially improving __int and __get_symbol init
61+
# Alt is handling the addl n, %esp/single instr not touching esp/subl n, %esp
62+
#
63+
# movl -n(%ebp), %eax
64+
# movl %eax, reg
65+
# popl %eax
66+
# movl %eax, (reg)
67+
# ->
68+
# movl -n(%ebp), reg
69+
# popl %eax
70+
# movl %eax, (reg)
71+
#
72+
#
73+
# subl x, %esp
74+
# op ?, reg
75+
# subl y, %esp
76+
# ->
77+
# subl x+y, %esp
78+
# op ?, reg
79+
#
80+
#
81+
# This *can't* be optimal:
82+
# movl n, %eax
83+
# cmpl %eax, %eax
84+
# setne %al
85+
# movzbl %al, %eax
86+
# testl %eax, %eax
87+
# je label
88+
#
89+
# Surely:
90+
# cmpl n, %eax
91+
# je label
92+
# must be enough?
93+
#
94+
#
95+
# movl n(%ebp), %esi
96+
# movl n(%ebp), %eax
97+
# must be a bug?
98+
#
99+
# movl -n(%ebp), %edx
100+
# pushl %edx
101+
# movl m(%ebp), %esi
102+
# popl %edi
103+
# movl %edi, o(%esi)
104+
# ->
105+
#movl m(%ebp), %esi
106+
#movl -n(%ebp), %edx
107+
#movl %edx, o(%esi)
108+
109+
def peephole
110+
return if @prev.empty?
111+
112+
args = @prev[-1]
113+
last = @prev[-2]
114+
115+
# subl $0, reg
116+
if match([:subl, 0, Symbol], args)
117+
@prev.pop
118+
return
119+
end
120+
121+
if args == [:movl, :eax, :eax]
122+
@prev.pop
123+
return
124+
end
125+
126+
if last
127+
if last == [:pushl, :ebx] && args == [:movl, "-4(%ebp)", :eax]
128+
@prev.pop
129+
@prev << [:movl, :ebx, :eax]
130+
return
131+
end
132+
133+
#if match([:movl, String, :eax], last) &&
134+
# match([:movl, :eax, String], args)
135+
# @prev.pop
136+
# @prev.pop
137+
# @prev << [:movl, last[1], args[2]]
138+
# return
139+
#end
140+
141+
if last == [:pushl, :eax] &&
142+
match([:popl, Symbol], args)
143+
# @unsafe Only ok because we know the compiler treats %eax as scratch
144+
@prev.pop
145+
@prev.pop
146+
@prev << [:movl, :eax, args[1]]
147+
return
148+
end
149+
150+
if match([:movl, Integer, :eax], last) &&
151+
match([:cmpl, :eax, Symbol], args)
152+
# @unsafe this optimization is ok only because we know the compiler treats
153+
# %eax as a scratch register
154+
@prev.pop
155+
@prev.pop
156+
@prev << [:cmpl, last[1], args[2]]
157+
return
158+
end
159+
160+
if last[0] == :pushl && args[0] == :popl && last[1] == args[1]
161+
@prev.pop
162+
@prev.pop
163+
return
164+
end
165+
166+
if args[0] == :subl &&
167+
last[1].is_a?(Integer) &&
168+
args[1].is_a?(Integer) && last[2] == args[2]
169+
170+
if last[0] == :addl &&
171+
# addl x, dest
172+
# subl y, dest
173+
@prev.pop
174+
@prev.pop
175+
if last[1] > args[1]
176+
@prev << [:addl, last[1] - args[1], args[2]]
177+
return
178+
elsif last[1] < args[1]
179+
@prev << [:subl, args[1] - last[1], args[2]]
180+
end
181+
return
182+
end
183+
184+
if last[0] == :subl
185+
# subl x, dest
186+
# subl y, dest
187+
@prev.pop
188+
@prev.pop
189+
@prev << [:subl, args[1] + last[1], args[2]]
190+
return
191+
end
192+
end
193+
194+
last2 = @prev[-3]
195+
if last2
196+
197+
if last2[0] == :movl
198+
if last2[2] == :eax
199+
# movl ???, %eax
200+
201+
if last2[1].class == Symbol
202+
src = last2[1]
203+
if last[0] == :movl && last[1] == :eax
204+
# movl reg, %eax
205+
# movl %eax, dest
206+
dest = last[2]
207+
208+
if args[0] == :movl && args[2] == :eax
209+
#movl reg, %eax
210+
#movl %eax, dest
211+
#movl ???, %eax
212+
#->
213+
#movl reg, dest
214+
#movl ???, %eax
215+
@prev.pop
216+
@prev.pop
217+
@prev.pop
218+
@prev << [:movl, src, dest]
219+
@prev << args
220+
return
221+
end
222+
end
223+
end
224+
225+
if last2[1].class == Symbol || last2[1].is_a?(Integer)
226+
val = last2[1]
227+
# movl Int|Reg, %eax
228+
229+
if last[0] == :subl
230+
if last[1] == :eax
231+
if last[2].class == Symbol
232+
reg = last[2]
233+
# movl $2, %eax
234+
# subl %eax, reg
235+
236+
if args[0] == :movl
237+
if args[1] != :eax
238+
if args[2] == :eax
239+
# movl $2, %eax
240+
# subl %eax, reg
241+
# movl ???, %eax
242+
@prev.pop
243+
@prev.pop
244+
@prev.pop
245+
@prev << [:subl, val, reg]
246+
@prev << args
247+
return
248+
end
249+
end
250+
end
251+
end
252+
end
253+
end
254+
end
255+
end
256+
end
257+
end
258+
end
259+
end
260+
261+
def emit(*args)
262+
@prev << args
263+
l = @prev.length
264+
while l > 0
265+
peephole
266+
return if @prev.length >= l
267+
l = @prev.length
268+
end
269+
end
270+
271+
def flush
272+
@prev.each do |row|
273+
@out.emit(*row)
274+
end
275+
@prev = []
276+
end
277+
end

0 commit comments

Comments
 (0)