|
|
@@ -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.')
|
|
|
@@ -31,57 +40,60 @@ def eliminate_common_subexpressions(block):
|
|
|
y = u
|
|
|
|
|
|
The algorithm used is as follows:
|
|
|
- - Traverse through the statements in reverse order.
|
|
|
+ - Traverse through the statements.
|
|
|
- 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, occurrences[0], 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)
|
|
|
+
|
|
|
+ # Insert the calculation before the original with the new
|
|
|
+ # destination address
|
|
|
+ block.insert(S('command', s.name, *([new_reg] + args)), \
|
|
|
+ index=occurrences[0])
|
|
|
|
|
|
- if last:
|
|
|
- # Insert an additional expression with a new destination address
|
|
|
- block.insert(S('command', s.name, *([new_reg] + args)), last)
|
|
|
+ changed = True
|
|
|
|
|
|
- # Replace the original expression with a move statement
|
|
|
- block.replace(1, [S('command', 'move', s[0], new_reg)])
|
|
|
- found = True
|
|
|
+ # Reset pointer to continue from the original statement
|
|
|
+ block.pointer = pointer
|
|
|
|
|
|
- block.reverse_statements()
|
|
|
+ return changed
|
|
|
|
|
|
- return found
|
|
|
-
|
|
|
|
|
|
def to_hex(value):
|
|
|
"""Create the hexadecimal string of an integer."""
|
|
|
@@ -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 = {}
|
|
|
@@ -125,7 +137,7 @@ def fold_constants(block):
|
|
|
|
|
|
if reg_from in register:
|
|
|
# Other value is also known, copy its value
|
|
|
- register[reg_to] = register[reg_to]
|
|
|
+ register[reg_to] = register[reg_from]
|
|
|
else:
|
|
|
# Other value is unknown, delete the value
|
|
|
del register[reg_to]
|
|
|
@@ -136,8 +148,9 @@ def fold_constants(block):
|
|
|
# Usage of variable with constant value
|
|
|
register[s[0]] = constants[s[1]]
|
|
|
elif s.name in ['addu', 'subu', 'mult', 'div']:
|
|
|
+ # TODO: implement 'mult' optimization
|
|
|
# Calculation with constants
|
|
|
- rd, rs, rt = s
|
|
|
+ rd, rs, rt = s[0], s[1], s[2]
|
|
|
rs_known = rs in register
|
|
|
rt_known = rt in register
|
|
|
|
|
|
@@ -162,23 +175,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):
|
|
|
@@ -210,12 +223,15 @@ def copy_propagation(block):
|
|
|
for i in xrange(len(moves_to)):
|
|
|
if moves_to[i] == s[0]:
|
|
|
moves_from[i] = s[1]
|
|
|
- break
|
|
|
- elif len(s) == 3 and s[0] in moves_to:
|
|
|
- # The result gets overwritten, so remove the data from the list.
|
|
|
+ continue
|
|
|
+ elif (len(s) == 3 or s.is_command('mlfo') or s.is_load()) \
|
|
|
+ and (s[0] in moves_to or s[0] in moves_from):
|
|
|
+ # One of the registers gets overwritten, so remove the data from
|
|
|
+ # the list.
|
|
|
i = 0
|
|
|
+
|
|
|
while i < len(moves_to):
|
|
|
- if moves_to[i] == s[0]:
|
|
|
+ if moves_to[i] == s[0] or moves_to[i] == s[1]:
|
|
|
del moves_to[i]
|
|
|
del moves_from[i]
|
|
|
else:
|
|
|
@@ -226,11 +242,11 @@ def copy_propagation(block):
|
|
|
for i in xrange(len(moves_to)):
|
|
|
if s[1] == moves_to[i]:
|
|
|
s[1] = moves_from[i]
|
|
|
- break
|
|
|
+ continue
|
|
|
|
|
|
if s[2] == moves_to[i]:
|
|
|
s[2] = moves_from[i]
|
|
|
- break
|
|
|
+ continue
|
|
|
|
|
|
changed = True
|
|
|
|
|
|
@@ -239,7 +255,7 @@ def copy_propagation(block):
|
|
|
|
|
|
def algebraic_transformations(block):
|
|
|
"""
|
|
|
- Change ineffective or useless algebraic transformations. Handled are:
|
|
|
+ Change ineffective or useless algebraic expressions. Handled are:
|
|
|
- x = y + 0 -> x = y
|
|
|
- x = y - 0 -> x = y
|
|
|
- x = y * 1 -> x = y
|
|
|
@@ -254,17 +270,62 @@ def algebraic_transformations(block):
|
|
|
if (s.is_command('addu') or s.is_command('subu')) and s[2] == 0:
|
|
|
block.replace(1, [S('command', 'move', s[0], s[1])])
|
|
|
changed = True
|
|
|
- elif s.is_command('mult') and s[2] == 1:
|
|
|
- block.replace(1, [S('command', 'move', s[0], s[1])])
|
|
|
- changed = True
|
|
|
- elif s.is_command('mult') and s[2] == 0:
|
|
|
- block.replace(1, [S('command', 'li', '$1', to_hex(0))])
|
|
|
- changed = True
|
|
|
elif s.is_command('mult'):
|
|
|
- shift_amount = log(s[2], 2)
|
|
|
- if shift_amount.is_integer():
|
|
|
- new_command = S('command', 'sll', s[0], s[1], shift_amount)
|
|
|
- block.replace(1, [new_command])
|
|
|
- changed = True
|
|
|
+ mflo = block.peek()
|
|
|
+
|
|
|
+ if mflo.is_command('mflo'):
|
|
|
+ if s[1] == 1:
|
|
|
+ block.replace(2, [S('command', 'move', mflo[0], s[0])])
|
|
|
+ changed = True
|
|
|
+ continue
|
|
|
+ elif s[1] == 0:
|
|
|
+ block.replace(2, [S('command', 'li', '$1', to_hex(0))])
|
|
|
+ changed = True
|
|
|
+ continue
|
|
|
+
|
|
|
+ shift_amount = log(s[1], 2)
|
|
|
+ if shift_amount.is_integer():
|
|
|
+ new_command = S('command', 'sll', \
|
|
|
+ mflo[0], s[0], \
|
|
|
+ int(shift_amount))
|
|
|
+ block.replace(2, [new_command])
|
|
|
+ changed = True
|
|
|
+
|
|
|
+ return changed
|
|
|
+
|
|
|
+
|
|
|
+def eliminate_dead_code(block):
|
|
|
+ """
|
|
|
+ Dead code elimination:
|
|
|
+ TODO: example...
|
|
|
+
|
|
|
+ The algorithm used is as follows:
|
|
|
+ - Traverse through the statements in reverse order.
|
|
|
+ - If the statement definition is dead, remove it. A variable is dead if it
|
|
|
+ is not used in the rest of the block, and is not in the `out' set of the
|
|
|
+ block.
|
|
|
+ """
|
|
|
+ # TODO: Finish
|
|
|
+ changed = False
|
|
|
+
|
|
|
+ block.reverse_statements()
|
|
|
+ unused = set()
|
|
|
+
|
|
|
+ while not block.end():
|
|
|
+ s = block.read()
|
|
|
+
|
|
|
+ for reg in s.get_def():
|
|
|
+ if reg in unused:
|
|
|
+ # Statement is redefined later, so this statement is useless
|
|
|
+ s.remove = True
|
|
|
+ #print 'reg %s is in %s, remove:' % (reg, unused), \
|
|
|
+ # block.pointer - 1, s
|
|
|
+ else:
|
|
|
+ unused.add(reg)
|
|
|
+
|
|
|
+ unused -= set(s.get_use())
|
|
|
+
|
|
|
+ block.apply_filter(lambda s: not hasattr(s, 'remove'))
|
|
|
+ block.reverse_statements()
|
|
|
|
|
|
return changed
|