Commit 06bb955c authored by Taddeus Kroes's avatar Taddeus Kroes

Merge branch 'master' of github.com:taddeus/peephole

parents e340aadb 134aea82
......@@ -2,6 +2,10 @@
*.pdf
*.pyc
*~
*.aux
*.log
*.out
*.toc
.coverage
coverage/
build/
......
Common subexpression elimination
Loop through statements of each block
for each binary operator, look back for usage of rs and rt. If rs or rt are
assigned, break. If exact same operator is found, add it to the list of
common subexpressions. If you reach the end of the block, or rs or rt are
assigned, make new destination address, and change each occurence of this
expression with a move from the new register address.
Jump/branch end of basoc block, Label begin of BB
instr writing to register and immediatly overwritten by second instruction
(general case of second optimization in table practicum assignment)
.file 1 "acron.c"
# GNU C 2.7.2.3 [AL 1.1, MM 40, tma 0.1] SimpleScalar running sstrix compiled by GNU C
# Cc1 defaults:
# -mgas -mgpOPT
# Cc1 arguments (-G value = 8, Cpu = default, ISA = 1):
# -quiet -dumpbase -O0 -o
gcc2_compiled.:
__gnu_compiled_c:
.globl w
.data
.align 2
w:
.word $LC0
.word $LC1
.word $LC2
.word $LC3
.word $LC4
.word $LC5
.sdata
.align 2
$LC5:
.ascii "Eephole\000"
.rdata
.align 2
$LC4:
.ascii "Peephole\000"
.align 2
$LC3:
.ascii "Optimization\000"
.align 2
$LC2:
.ascii "Practicum\000"
.align 2
$LC1:
.ascii "Ertalerbouw\000"
.align 2
$LC0:
.ascii "Vertalerbouw\000"
.text
.align 2
.globl is_vowel
.sdata
.align 2
$LC6:
.ascii "%s\000"
.align 2
$LC7:
.ascii " %s\000"
.align 2
$LC8:
.ascii "\n\000"
.text
.align 2
.globl do_perm
.align 2
.globl main
.comm acron,12
.comm command,100
.comm done,24
.comm pindex,28
.text
.loc 1 10
.ent is_vowel
is_vowel:
.frame $fp,16,$31 # vars= 8, regs= 1/0, args= 0, extra= 0
.mask 0x40000000,-8
.fmask 0x00000000,0
subu $sp,$sp,16
sw $fp,8($sp)
move $fp,$sp
move $3,$4
sb $3,0($fp)
move $2,$0
lb $4,0($fp)
li $5,0x00000041 # 65
beq $4,$5,$L3
lb $4,0($fp)
li $5,0x00000045 # 69
beq $4,$5,$L3
lb $4,0($fp)
li $5,0x00000049 # 73
beq $4,$5,$L3
lb $4,0($fp)
li $5,0x0000004f # 79
beq $4,$5,$L3
lb $4,0($fp)
li $5,0x00000055 # 85
beq $4,$5,$L3
lb $4,0($fp)
li $5,0x00000059 # 89
bne $4,$5,$L2
$L3:
li $2,0x00000001 # 1
$L2:
j $L1
$L1:
move $sp,$fp # sp not trusted here
lw $fp,8($sp)
addu $sp,$sp,16
j $31
.end is_vowel
.loc 1 15
.ent do_perm
do_perm:
.frame $fp,56,$31 # vars= 24, regs= 4/0, args= 16, extra= 0
.mask 0xc0030000,-4
.fmask 0x00000000,0
subu $sp,$sp,56
sw $31,52($sp)
sw $fp,48($sp)
sw $17,44($sp)
sw $16,40($sp)
move $fp,$sp
sw $4,56($fp)
sw $5,60($fp)
sw $6,64($fp)
sw $7,68($fp)
sw $0,24($fp)
lw $2,64($fp)
li $3,0x00000001 # 1
bne $2,$3,$L5
lw $2,pindex
move $3,$2
sll $2,$3,2
la $3,w
addu $2,$2,$3
lw $3,0($2)
lb $4,0($3)
jal is_vowel
bne $2,$0,$L5
lw $2,56($fp)
move $3,$2
sll $2,$3,2
la $3,w
addu $2,$2,$3
lw $3,0($2)
lb $4,0($3)
jal is_vowel
beq $2,$0,$L4
$L5:
lw $2,64($fp)
slt $3,$2,2
bne $3,$0,$L6
lw $2,64($fp)
move $3,$2
sll $2,$3,2
la $3,pindex-8
addu $2,$2,$3
lw $3,0($2)
move $2,$3
sll $3,$2,2
la $4,w
addu $2,$3,$4
lw $3,0($2)
lb $4,0($3)
jal is_vowel
move $16,$2
lw $2,64($fp)
move $3,$2
sll $2,$3,2
la $3,pindex-4
addu $2,$2,$3
lw $3,0($2)
move $2,$3
sll $3,$2,2
la $4,w
addu $2,$3,$4
lw $3,0($2)
lb $4,0($3)
jal is_vowel
move $17,$2
lw $2,56($fp)
move $3,$2
sll $2,$3,2
la $3,w
addu $2,$2,$3
lw $3,0($2)
lb $4,0($3)
jal is_vowel
addu $3,$16,$17
addu $2,$3,$2
sw $2,24($fp)
lw $2,24($fp)
beq $2,$0,$L8
lw $2,24($fp)
li $3,0x00000003 # 3
bne $2,$3,$L7
$L8:
j $L4
$L7:
$L6:
lw $2,64($fp)
addu $3,$2,1
sw $3,64($fp)
sll $3,$2,2
la $4,pindex
addu $2,$3,$4
lw $3,56($fp)
sw $3,0($2)
lw $2,64($fp)
slt $3,$2,6
beq $3,$0,$L9
lw $3,68($fp)
subu $2,$3,1
move $3,$2
sw $3,68($fp)
beq $3,$0,$L9
sw $0,16($fp)
$L10:
lw $2,16($fp)
slt $3,$2,6
beq $3,$0,$L11
$L13:
lw $2,16($fp)
move $3,$2
sll $2,$3,2
lw $3,60($fp)
addu $2,$2,$3
lw $3,0($2)
bne $3,$0,$L14
lw $2,16($fp)
move $3,$2
sll $2,$3,2
lw $3,60($fp)
addu $2,$2,$3
li $3,0x00000001 # 1
sw $3,0($2)
lw $4,16($fp)
lw $5,60($fp)
lw $6,64($fp)
lw $7,68($fp)
jal do_perm
lw $2,16($fp)
move $3,$2
sll $2,$3,2
lw $3,60($fp)
addu $2,$2,$3
sw $0,0($2)
$L14:
$L12:
lw $3,16($fp)
addu $2,$3,1
move $3,$2
sw $3,16($fp)
j $L10
$L11:
j $L15
$L9:
sw $0,28($fp)
sw $0,20($fp)
$L16:
lw $2,20($fp)
lw $3,64($fp)
slt $2,$2,$3
beq $2,$0,$L17
$L19:
sw $0,32($fp)
$L20:
lw $2,20($fp)
move $3,$2
sll $2,$3,2
la $3,pindex
addu $2,$2,$3
lw $3,0($2)
move $2,$3
sll $3,$2,2
la $4,w
addu $2,$3,$4
lw $3,0($2)
lw $4,32($fp)
addu $2,$3,$4
lb $4,0($2)
jal isupper
beq $2,$0,$L21
$L22:
lw $2,28($fp)
addu $3,$2,1
sw $3,28($fp)
lw $3,20($fp)
move $4,$3
sll $3,$4,2
la $4,pindex
addu $3,$3,$4
lw $4,0($3)
move $3,$4
sll $4,$3,2
la $5,w
addu $3,$4,$5
lw $4,32($fp)
addu $5,$4,1
sw $5,32($fp)
lw $5,0($3)
addu $3,$4,$5
lbu $4,0($3)
sb $4,acron($2)
j $L20
$L21:
$L18:
lw $3,20($fp)
addu $2,$3,1
move $3,$2
sw $3,20($fp)
j $L16
$L17:
lw $2,28($fp)
la $3,acron
addu $2,$2,$3
sb $0,0($2)
la $4,$LC6
la $5,acron
jal printf
sw $0,20($fp)
$L23:
lw $2,20($fp)
lw $3,64($fp)
slt $2,$2,$3
beq $2,$0,$L24
$L26:
lw $2,20($fp)
move $3,$2
sll $2,$3,2
la $3,pindex
addu $2,$2,$3
lw $3,0($2)
move $2,$3
sll $3,$2,2
la $4,w
addu $2,$3,$4
la $4,$LC7
lw $5,0($2)
jal printf
$L25:
lw $3,20($fp)
addu $2,$3,1
move $3,$2
sw $3,20($fp)
j $L23
$L24:
la $4,$LC8
jal printf
$L15:
$L4:
move $sp,$fp # sp not trusted here
lw $31,52($sp)
lw $fp,48($sp)
lw $17,44($sp)
lw $16,40($sp)
addu $sp,$sp,56
j $31
.end do_perm
.loc 1 53
.ent main
main:
.frame $fp,32,$31 # vars= 8, regs= 2/0, args= 16, extra= 0
.mask 0xc0000000,-4
.fmask 0x00000000,0
subu $sp,$sp,32
sw $31,28($sp)
sw $fp,24($sp)
move $fp,$sp
jal __main
li $2,0x00000004 # 4
sw $2,20($fp)
$L28:
lw $2,20($fp)
slt $3,$2,7
beq $3,$0,$L29
$L31:
sw $0,16($fp)
$L32:
lw $2,16($fp)
slt $3,$2,6
beq $3,$0,$L33
$L35:
lw $2,16($fp)
move $3,$2
sll $2,$3,2
la $3,done
addu $2,$2,$3
li $3,0x00000001 # 1
sw $3,0($2)
lw $4,16($fp)
la $5,done
move $6,$0
lw $7,20($fp)
jal do_perm
lw $2,16($fp)
move $3,$2
sll $2,$3,2
la $3,done
addu $2,$2,$3
sw $0,0($2)
$L34:
lw $3,16($fp)
addu $2,$3,1
move $3,$2
sw $3,16($fp)
j $L32
$L33:
$L30:
lw $3,20($fp)
addu $2,$3,1
move $3,$2
sw $3,20($fp)
j $L28
$L29:
$L27:
move $sp,$fp # sp not trusted here
lw $31,28($sp)
lw $fp,24($sp)
addu $sp,$sp,32
j $31
.end main
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
.file 1 "pi.c"
# GNU C 2.7.2.3 [AL 1.1, MM 40, tma 0.1] SimpleScalar running sstrix compiled by GNU C
# Cc1 defaults:
# -mgas -mgpOPT
# Cc1 arguments (-G value = 8, Cpu = default, ISA = 1):
# -quiet -dumpbase -o
gcc2_compiled.:
__gnu_compiled_c:
.rdata
.align 2
$LC0:
.ascii "Usage: %s <iterations>\n\000"
.sdata
.align 2
$LC3:
.ascii "%.10f\n\000"
.align 3
$LC1:
.word 0xffc00000 # 2147483647
.word 0x41dfffff
.align 3
$LC2:
.word 0x00000000 # 1
.word 0x3ff00000
.align 3
$LC4:
.word 0x00000000 # 4
.word 0x40100000
.text
.align 2
.globl main
.extern stderr, 4
.text
.loc 1 5
.ent main
main:
.frame $fp,56,$31 # vars= 32, regs= 2/0, args= 16, extra= 0
.mask 0xc0000000,-4
.fmask 0x00000000,0
subu $sp,$sp,56
sw $31,52($sp)
sw $fp,48($sp)
move $fp,$sp
sw $4,56($fp)
sw $5,60($fp)
jal __main
sw $0,24($fp)
lw $2,56($fp)
li $3,0x00000002 # 2
beq $2,$3,$L2
lw $2,60($fp)
lw $4,stderr
la $5,$LC0
lw $6,0($2)
jal fprintf
move $4,$0
jal exit
$L2:
lw $3,60($fp)
addu $2,$3,4
lw $4,0($2)
jal atoi
sw $2,20($fp)
li $4,0x00000001 # 1
jal srandom
sw $0,16($fp)
$L3:
lw $2,16($fp)
lw $3,20($fp)
slt $2,$2,$3
bne $2,$0,$L6
j $L4
$L6:
jal random
mtc1 $2,$f0
#nop
cvt.d.w $f0,$f0
l.d $f2,$LC1
div.d $f0,$f0,$f2
s.d $f0,32($fp)
jal random
mtc1 $2,$f0
#nop
cvt.d.w $f0,$f0
l.d $f2,$LC1
div.d $f0,$f0,$f2
s.d $f0,40($fp)
l.d $f0,32($fp)
l.d $f2,32($fp)
mul.d $f0,$f0,$f2
l.d $f2,40($fp)
l.d $f4,40($fp)
mul.d $f2,$f2,$f4
add.d $f0,$f0,$f2
l.d $f2,$LC2
c.le.d $f0,$f2
bc1f $L7
lw $3,24($fp)
addu $2,$3,1
move $3,$2
sw $3,24($fp)
$L7:
$L5:
lw $3,16($fp)
addu $2,$3,1
move $3,$2
sw $3,16($fp)
j $L3
$L4:
l.s $f0,24($fp)
#nop
cvt.d.w $f0,$f0
l.s $f2,20($fp)
#nop
cvt.d.w $f2,$f2
div.d $f0,$f0,$f2
l.d $f2,$LC4
mul.d $f0,$f0,$f2
la $4,$LC3
dmfc1 $6,$f0
jal printf
li $2,0x00000001 # 1
j $L1
$L1:
move $sp,$fp # sp not trusted here
lw $31,52($sp)
lw $fp,48($sp)
addu $sp,$sp,56
j $31
.end main
\ No newline at end of file
This diff is collapsed.
RM=rm -rf
all: report.pdf
%.pdf: %.tex
pdflatex $^
pdflatex $^
clean:
$(RM) *.pdf *.aux *.log *.out *.toc *.snm *.nav
......@@ -11,39 +11,188 @@
\usepackage{hyperref}
\title{Peephole Optimizer}
\author{Jayke Meijer (6049885), Richard Torenvliet (6138861), Taddeus Kroes (6054129)}
\author{Jayke Meijer (6049885), Richard Torenvliet (6138861), Tadde\"us Kroes
(6054129)}
\begin{document}
\maketitle
\pagebreak
\tableofcontents
\pagebreak
\section{Introduction}
The goal of the assignment is to implement the optimization stage of the compiler. To reach this goal the parser part of the compiler has to be implemented.
The output of the gcc cross compiler on a c program is our input, the output of the gcc cross compiler is in the form of Assembly code, but not optimized. Our assignment includes a number of c programs, an important part of the assignment is parsing the data. Parsing the data is done with lex and yacc. The lexer is a program that finds keywords that meets the regular expression provided in the lexer. After the lexer, the yaccer takes over. Yaccer can turn the keywords in to an action.
The goal of the assignment is to implement the optimization stage of the
compiler. To reach this goal the parser and the optimizer part of the compiler
have to be implemented.
The output of the xgcc cross compiler on a C program is our input. The output
of the xgcc cross compiler is in the form of Assembly code, but not optimized.
Our assignment includes a number of C programs. An important part of the
assignment is parsing the data. Parsing the data is done with Lex and Yacc. The
Lexer is a program that finds keywords that meets the regular expression
provided in the Lexer. After the Lexer, the Yaccer takes over. Yacc can turn
the keywords in to an action.
\section{Design}
There are two general types of of optimizations of the assembly code, global
optimizations and optimizations on a so-called basic block. These optimizations
will be discussed seperatly
\section{Design \& Implementation}
We decided to implement the optimization in python. We chose this programming language because python is an easy language to manipulate strings, work objective ori\"ented etc.
It turns out that a lex and yacc are also implemented in a python version, named PLY(Python Lex-Yacc). This allows us to use one language, Python, instead of two i.e. C and Python. Also no debugging is needed in C, only in Python which makes our assignment more feasible.
\subsection{Global optimizations}
\subsection{Design}
We only perform one global optimization, which is optimizing branch-jump
statements. The unoptimized Assembly code contains sequences of code of the
following structure:
\begin{lstlisting}
beq ...,$Lx
j $Ly
$Lx: ...\end{lstlisting}
This is inefficient, since there is a jump to a label that follows this code.
It would be more efficient to replace the branch statement with a \texttt{bne}
(the opposite case) to the label used in the jump statement. This way the jump
statement can be eliminated, since the next label follows anyway. The same can
of course be done for the opposite case, where a \texttt{bne} is changed into a
\texttt{beq}.
Since this optimization is done between two series of codes with jumps and
labels, we can not perform this code during the basic block optimizations. The
reason for this will become clearer in the following section.
\subsection*{Implementation}
This
\subsection{Basic Block Optimizations}
\subsubsection*{PLY}
Optimizations on basic blocks are a more important part of the optimizer.
First, what is a basic block? A basic block is a sequence of statements
guaranteed to be executed in that order, and that order alone. This is the case
for a piece of code not containing any branches or jumps.
\section{Results}
To create a basic block, you need to define what is the leader of a basic
block. We call a statement a leader if it is either a jump/branch statement, or
the target of such a statement. Then a basic block runs from one leader
(not including this leader) until the next leader (including this leader). !!!!
There are quite a few optimizations we perform on these basic blocks, so we
will describe the types of optimizations here in stead of each optimization.
\subsubsection*{Standard peephole optimizations}
These are optimizations that simply look for a certain statement or pattern of
statements, and optimize these. For example,
\begin{lstlisting}
mov $regA,$regB
instr $regA, $regA,...
\end{lstlisting}
can be optimized into
\begin{lstlisting}
instr $regA, $regB,...
\end{lstlisting}
since the register \texttt{\$regA} gets overwritten by the second instruction
anyway, and the instruction can easily use \texttt{\$regB} in stead of
\texttt{\$regA}. There are a few more of these cases, which are the same as
those described on the practicum page
\footnote{\url{http://staff.science.uva.nl/~andy/compiler/prac.html}} and in
Appendix \ref{opt}.
\subsubsection*{Common subexpression elimination}
A more advanced optimization is common subexpression elimination. This means
that expensive operations as a multiplication or addition are performed only
once and the result is then `copied' into variables where needed.
A standard method for doing this is the creation of a DAG or Directed Acyclic
Graph. However, this requires a fairly advanced implementation. Our
implementation is a slightly less fancy, but easier to implement.
We search from the end of the block up for instructions that are eligible for
CSE. If we find one, we check further up in the code for the same instruction,
and add that to a temporary storage list. This is done until the beginning of
the block or until one of the arguments of this expression is assigned. Now all
occurences of this expression can be replaced by a move of a new variable that
is generated above the first occurence, which contains the value of the
expression.
This is a less efficient method, but because the basic blocks are in general
not very large and the exectution time of the optimizer is not a primary
concern, this is not a big problem.
\section{Implementation}
\subsection*{pi.c}
We decided to implement the optimization in Python. We chose this programming
language because Python is an easy language to manipulate strings, work
object-oriented etc.
It turns out that a Lex and Yacc are also available as a Python module,
named PLY(Python Lex-Yacc). This allows us to use one language, Python, instead
of two, i.e. C and Python. Also no debugging is needed in C, only in Python
which makes our assignment more feasible.
\subsection*{arcron.c}
The program has three steps, parsing the Assembly code into a datastructure we
can use, the so-called Intermediate Representation, performing optimizations on
this IR and writing the IR back to Assembly.
\subsection*{whet.c}
\subsection{Parsing with PLY}
\subsection*{slalom.c}
\subsection*{clinpack.c}
\section{conclusion}
\end{document}
\ No newline at end of file
\subsection{Optimizations}
\subsection{Writing}
\section{Results}
\subsection{pi.c}
\subsection{arcron.c}
\subsection{whet.c}
\subsection{slalom.c}
\subsection{clinpack.c}
\section{Conclusion}
\appendix
\section{Total list of optimizations}
\label{opt}
\textbf{Global optimizations}
\begin{tabular}{| c c c |}
\hline
\begin{lstlisting}
beq ...,$Lx
j $Ly
$Lx: ...\end{lstlisting} & $\Rightarrow$ & \begin{lstlisting}
bne ...,$Ly
$Lx: ...\end{lstlisting}\\
\hline
\begin{lstlisting}
bne ...,$Lx
j $Ly
$Lx: ...\end{lstlisting} & $\Rightarrow$ & \begin{lstlisting}
beq ...,$Ly
$Lx: ...\end{lstlisting}\\
\hline
\end{tabular}\\
\\
\textbf{Simple basic block optimizations}
\begin{tabular}{|c c c|}
\hline
\begin{lstlisting}
beq ...,$Lx
j $Ly
$Lx: ...\end{lstlisting} & $\Rightarrow$ & \begin{lstlisting}
bne ...,$Ly
$Lx: ...\end{lstlisting}\\
\hline
\end{tabular}\\
\\
\textbf{Advanced basic block optimizations}
\end{document}
......@@ -97,55 +97,102 @@ def generate_flow_graph(blocks):
b.add_edge_to(blocks[i + 1])
def generate_dominator_tree(nodes):
"""Add dominator administration to the given flow graph nodes."""
# Dominator of the start node is the start itself
nodes[0].dom = set([nodes[0]])
# For all other nodes, set all nodes as the dominators
for n in nodes[1:]:
n.dom = set(copy(nodes))
def pred(n, known=[]):
"""Recursively find all predecessors of a node."""
direct = filter(lambda x: x not in known, n.edges_from)
p = copy(direct)
for ancestor in direct:
p += pred(ancestor, direct)
return p
# Iteratively eliminate nodes that are not dominators
changed = True
while changed:
changed = False
for n in nodes[1:]:
old_dom = n.dom
intersection = lambda p1, p2: p1.dom & p2.dom
n.dom = set([n]) | reduce(intersection, pred(n), set([]))
if n.dom != old_dom:
changed = True
def idom(d, n):
"""Check if d immediately dominates n."""
for b in n.dom:
if b != d and b != n and b in n.dom:
return False
return True
# Build tree using immediate dominators
for n in nodes:
for d in n.dom:
if idom(d, n):
d.set_dominates(n)
break
# statements = parse_file(...)
# b = find_basic_blocks(statements)
# generate_flow_graph(b) # nodes now have edges
# generate_dominator_tree(b) # nodes now have dominators
#def generate_dominator_tree(nodes):
# """Add dominator administration to the given flow graph nodes."""
# # Dominator of the start node is the start itself
# nodes[0].dom = set([nodes[0]])
#
# # For all other nodes, set all nodes as the dominators
# for n in nodes[1:]:
# n.dom = set(copy(nodes))
#
# def pred(n, known=[]):
# """Recursively find all predecessors of a node."""
# direct = filter(lambda x: x not in known, n.edges_from)
# p = copy(direct)
#
# for ancestor in direct:
# p += pred(ancestor, direct)
#
# return p
#
# # Iteratively eliminate nodes that are not dominators
# changed = True
#
# while changed:
# changed = False
#
# for n in nodes[1:]:
# old_dom = n.dom
# intersection = lambda p1, p2: p1.dom & p2.dom
# n.dom = set([n]) | reduce(intersection, pred(n), set([]))
#
# if n.dom != old_dom:
# changed = True
#
# def idom(d, n):
# """Check if d immediately dominates n."""
# for b in n.dom:
# if b != d and b != n and b in n.dom:
# return False
#
# return True
#
# # Build tree using immediate dominators
# for n in nodes:
# for d in n.dom:
# if idom(d, n):
# d.set_dominates(n)
# break
class Dag:
def __init__(self, block):
"""Create the Directed Acyclic Graph of all binary operations in a
basic block."""
self.nodes = []
for s in block:
if s.is_command('move') or s.is_monop():
rd, rs = s
y = self.find_reg_node(rs)
self.find_op_node(s.name, rd, y)
elif s.is_binop():
rd, rs, rt = s
y = self.find_reg_node(rs)
z = self.find_reg_node(rt)
self.find_op_node(s.name, rd, y, z)
def find_reg_node(self, reg):
for n in self.nodes:
if reg in n.reg:
return n
node = DagLeaf(reg)
self.nodes.append(node)
return node
def find_op_node(self, op, rd, *args):
for n in self.nodes:
if not isinstance(n, DagLeaf) and n.op == op and n.nodes == args:
n.labels.append(rd)
return n
node = DagNode(op, rd, *args)
self.nodes.append(node)
return node
class DagNode:
def __init__(self, op, label, *args):
self.op = op
self.labels = [label]
self.nodes = args
class DagLeaf:
def __init__(self, reg):
self.reg = reg
......@@ -7,6 +7,45 @@ def optimize_global(statements):
"""Optimize statement sequences on a global level."""
old_len = -1
while old_len != len(statements):
old_len = len(statements)
while not statements.end():
s = statements.read()
# beq/bne ..., $Lx -> bne/beq ..., $Ly
# j $Ly $Lx:
# $Lx:
if s.is_command('beq') or s.is_command('bne'):
following = statements.peek(2)
if len(following) == 2:
j, label = following
if j.is_command('j') and label.is_label(s[2]):
if s.is_command('beq'):
s.name = 'bne'
else:
s.name = 'beq'
s[2] = j[0]
statements.replace(3, [s, label])
def optimize_blocks(blocks):
"""Call the optimizer for each basic block. Do this several times until
no more optimizations are achieved."""
optimized = []
for block in blocks:
optimize_block(block)
return blocks
def optimize_block(statements):
"""Optimize a basic block."""
old_len = -1
while old_len != len(statements):
old_len = len(statements)
......@@ -67,54 +106,15 @@ def optimize_global(statements):
lw[-1] = str(s[2]) + lw[-1][1:]
statements.replace(2, [lw])
continue
# move $RegA, $RegB -> move $RegA, $RegB
# move $RegB, $RegA
if s.is_command('move'):
move = statements.peek()
# beq ..., $Lx -> bne ..., $Ly
# j $Ly $Lx:
# $Lx:
if s.is_command('beq'):
following = statements.peek(2)
if len(following) == 2:
j, label = following
if j.is_command('j') and label.is_label(s[2]):
s.name = 'bne'
s[2] = j[0]
statements.replace(3, [s, label])
def optimize_blocks(blocks):
"""Call the optimizer for each basic block. Do this several times until
no more optimizations are achieved."""
changed = True
while changed:
changed = False
optimized = []
for block in blocks:
block_changed, b = optimize_block(block)
optimized.append(b)
if block_changed:
changed = True
blocks = optimized
return reduce(lambda a, b: a + b, blocks, [])
def optimize_block(statements):
"""Optimize a basic block."""
changed = False
output_statements = []
for statement in statements:
new_statement = statement
output_statements.append(new_statement)
return changed, output_statements
if move.is_command('move') and move[0] == s[1] and \
move[1] == s[0]:
statements.replace(2, [s])
def optimize(statements, verbose=0):
......@@ -143,7 +143,7 @@ def optimize(statements, verbose=0):
print 'Original statements: %d' % o
print 'After global optimization: %d' % g
print 'After basic blocks optimization: %d' % b
print 'Speedup: %d (%d%%)' \
% (b - o, int((b - o) / o * 100))
print 'Optimization: %d (%d%%)' \
% (b - o, int((b - o) / float(o) * 100))
return opt_blocks
......@@ -32,7 +32,7 @@ def t_DIRECTIVE(t):
return t
def t_hex_word(t):
r'0x[0-9a-fA-F]{8}'
r'0x([0-9a-fA-F]{8}|[0-9a-fA-F]{4})'
t.type = 'WORD'
return t
......@@ -47,7 +47,7 @@ def t_int(t):
return t
def t_WORD(t):
r'[a-zA-Z0-9$_.+()]+'
r'[a-zA-Z0-9$_.+()-]+'
return t
# Ignore whitespaces
......
......@@ -21,7 +21,7 @@ class Statement:
arguments."""
return self.stype == other.stype and self.name == other.name \
and self.args == other.args
def __len__(self):
return len(self.args)
......@@ -74,6 +74,14 @@ class Statement:
return self.is_command() \
and re.match('^(add|sub|mult|div|abs|neg)(u|\.d)?$', self.name)
def is_monop(self):
"""Check if the statement is an unary operation."""
return len(self) == 2 and self.is_arith()
def is_binop(self):
"""Check if the statement is an binary operation."""
return self.is_command() and len(self) == 3 and not self.is_jump()
def jump_target(self):
"""Get the jump target of this statement."""
if not self.is_jump():
......@@ -110,7 +118,10 @@ class Block:
def peek(self, count=1):
"""Read the statements until an offset from the current pointer
position."""
position."""
if self.end():
return Statement('empty', '') if count == 1 else []
return self.statements[self.pointer] if count == 1 \
else self.statements[self.pointer:self.pointer + count]
......@@ -120,7 +131,7 @@ class Block:
replacement."""
if self.pointer == 0:
raise Exception('No statement have been read yet.')
if start == None:
start = self.pointer - 1
......
......@@ -12,13 +12,15 @@ def write_statements(statements):
if s.is_label():
line = s.name + ':'
indent_level = 1
elif s.is_inline_comment():
line = '#' + s.name
l = len(prevline.expandtabs(4))
tabs = int(ceil((24 - l) / 4.)) + 1
newline = '\t' * tabs
elif s.is_comment():
line = '\t' * indent_level + line
line = '#' + s.name
if s.is_inline_comment():
l = len(prevline.expandtabs(4))
tabs = int(ceil((24 - l) / 4.)) + 1
newline = '\t' * tabs
else:
line = '\t' * indent_level + line
elif s.is_directive():
line = '\t' + s.name
elif s.is_command():
......
......@@ -2,7 +2,7 @@ import unittest
from src.statement import Statement as S
from src.dataflow import BasicBlock as B, find_leaders, find_basic_blocks, \
generate_flow_graph
generate_flow_graph, Dag, DagNode, DagLeaf
class TestDataflow(unittest.TestCase):
......@@ -44,3 +44,56 @@ class TestDataflow(unittest.TestCase):
self.assertEqual(b2.edges_to, [b3])
self.assertIn(b1, b3.edges_from)
self.assertIn(b2, b3.edges_from)
def test_dag_unary(self):
dag = Dag(B([S('command', 'neg.d', '$rd', '$rs')]))
expect = Dag([])
expect.nodes = [DagLeaf('$rs'), DagNode('neg.d', '$rd', DagLeaf('$rs'))]
self.assertEqualDag(dag, expect)
def test_dag_binary(self):
dag = Dag(B([S('command', 'addu', '$rd', '$r1', '$r2')]))
expect = Dag([])
expect.nodes = [DagLeaf('$r1'),
DagLeaf('$r2'),
DagNode('addu', '$rd', DagLeaf('$r1'), DagLeaf('$r2'))]
self.assertEqualDag(dag, expect)
# def test_dag_combinednode(self):
# dag = Dag(B([S('command', 'mult', '$rd1', '$r1', '$r2'),
# S('command', 'mult', '$rd2', '$r1', '$r2')]))
# expect = Dag([])
# multnode = DagNode('mult',
# DagLeaf('$r1'),
# DagLeaf('$r2'))
# multnode.labels = ['$rd1', '$rd2']
# expect.nodes = [DagLeaf('$r1'),
# DagLeaf('$r2'),
# multnode]
#
# self.assertEqualDag(dag, expect)
def assertEqualDag(self, dag1, dag2):
self.assertEqual(len(dag1.nodes), len(dag2.nodes))
for node1, node2 in zip(dag1.nodes, dag2.nodes):
self.assertEqualNodes(node1, node2)
def assertEqualNodes(self, node1, node2):
if isinstance(node1, DagLeaf):
self.assertIsInstance(node2, DagLeaf)
self.assertEqual(node1.reg, node2.reg)
elif isinstance(node2, DagLeaf):
raise AssertionError
else:
self.assertEqual(node1.op , node2.op)
self.assertEqual(node1.labels, node2.labels)
self.assertEqual(len(node1.nodes), len(node2.nodes))
for child1, child2 in zip(node1.nodes, node2.nodes):
self.assertEqualNodes(child1, child2)
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment