Commit 950105c0 authored by Taddeus Kroes's avatar Taddeus Kroes

Fixed Common Subexpression Elimination.

parent cad60b16
......@@ -2,22 +2,31 @@ from src.statement import Statement as S
from math import log
def reg_dead_in(var, context):
"""Check if a register is `dead' in a given list of statements."""
# TODO: Finish
for s in context:
if s.defines(var) or s.uses(var):
def reg_can_be_used_in(reg, block, start, end):
"""Check if a register addres safely be used in a block section using local
dataflow analysis."""
# Check if the register used or defined in the block section
for s in block[start:end]:
if s.uses(reg) or s.defines(reg):
return False
# Check if the register is used inside the block after the specified
# section, without having been re-assigned first
for s in block[end:]:
if s.uses(reg):
return False
elif s.defines(reg):
return True
return True
def find_free_reg(context):
def find_free_reg(block, start, end):
"""Find a temporary register that is free in a given list of statements."""
for i in xrange(8):
tmp = '$t%d' % i
for i in xrange(8, 16):
tmp = '$%d' % i
if reg_dead_in(tmp, context):
if reg_can_be_used_in(tmp, block, start, end):
return tmp
raise Exception('No temporary register is available.')
......@@ -35,52 +44,55 @@ def eliminate_common_subexpressions(block):
- If the statement can be possibly be eliminated, walk further collecting
all other occurrences of the expression until one of the arguments is
assigned in a statement, or the start of the block has been reached.
- If one or more occurrences were found, insert the expression with a new
destination address before the last found occurrence and change all
- If one or more occurrences were changed, insert the expression with a new
destination address before the last changed occurrence and change all
occurrences to a move instruction from that address.
"""
found = False
block.reverse_statements()
changed = False
while not block.end():
s = block.read()
if s.is_arith():
pointer = block.pointer
last = False
new_reg = False
occurrences = [pointer - 1]
args = s[1:]
# Collect similar statements
while not block.end():
s2 = block.read()
if not s2.is_command():
continue
# Stop if one of the arguments is assigned
if len(s2) and s2[0] in args:
break
# Replace a similar expression by a move instruction
if s2.name == s.name and s2[1:] == args:
if not new_reg:
new_reg = find_free_reg(block[:pointer])
occurrences.append(block.pointer - 1)
block.replace(1, [S('command', 'move', s2[0], new_reg)])
last = block.pointer
if len(occurrences) > 1:
new_reg = find_free_reg(block, pointer, occurrences[-1])
# Reset pointer to and continue from the original statement
block.pointer = pointer
# Replace all occurrences with a move statement
for occurrence in occurrences:
rd = block[occurrence][0]
block.replace(1, [S('command', 'move', rd, new_reg)], \
start=occurrence)
if last:
# Insert an additional expression with a new destination address
block.insert(S('command', s.name, *([new_reg] + args)), last)
# Insert the calculation before the original with the new
# destination address
block.insert(S('command', s.name, *([new_reg] + args)), \
index=occurrences[0])
# Replace the original expression with a move statement
block.replace(1, [S('command', 'move', s[0], new_reg)])
found = True
changed = True
block.reverse_statements()
# Reset pointer to continue from the original statement
block.pointer = pointer
return found
return changed
def to_hex(value):
......@@ -103,7 +115,7 @@ def fold_constants(block):
- When a variable is used, the following happens:
lw $reg, VAR -> register[$reg] = constants[VAR]
"""
found = False
changed = False
# Variable values
constants = {}
......@@ -162,23 +174,23 @@ def fold_constants(block):
block.replace(1, [S('command', 'li', rd, result)])
register[rd] = result
found = True
changed = True
elif rt_known:
# c = 10 -> b = a + 10
# b = c + a
s[2] = register[rt]
found = True
elif rs_known and s.name in ['addu', 'mult']:
changed = True
elif rs_known and s.name == 'addu':
# a = 10 -> b = c + 10
# b = c + a
s[1] = rt
s[2] = register[rs]
found = True
changed = True
elif len(s) and s[0] in register:
# Known register is overwritten, remove its value
del register[s[0]]
return found
return changed
def copy_propagation(block):
......
......@@ -96,7 +96,23 @@ class Statement:
def uses(self, reg):
"""Check if this statement uses the given register."""
# TODO: Finish
return (self.is_load() or self.is_arith()) and reg in self[1:]
if self.is_arith():
return reg in self[1:]
if self.is_command('move'):
return self[1] == reg
if self.is_command('lw', 'sb', 'sw', 'dsw'):
m = re.match('^\d+\(([^)]+)\)$', self[1])
if m:
return m.group(1) == reg
# 'sw' also uses its first argument
if self.name in ['sw', 'dsw']:
return self[0] == reg
return False
class Block:
......
......@@ -19,9 +19,9 @@ class TestOptimizeAdvanced(unittest.TestCase):
def test_eliminate_common_subexpressions_simple(self):
b = B([S('command', 'addu', '$regC', '$regA', '$regB'),
S('command', 'addu', '$regD', '$regA', '$regB')])
e = [S('command', 'addu', '$t0', '$regA', '$regB'), \
S('command', 'move', '$regC', '$t0'), \
S('command', 'move', '$regD', '$t0')]
e = [S('command', 'addu', '$8', '$regA', '$regB'), \
S('command', 'move', '$regC', '$8'), \
S('command', 'move', '$regD', '$8')]
eliminate_common_subexpressions(b)
self.assertEqual(b.statements, e)
......
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