Commit 7018b528 authored by Taddeus Kroes's avatar Taddeus Kroes

Fixed liveness analysis.

parent 770cbc9f
...@@ -23,55 +23,59 @@ class BasicBlock(Block): ...@@ -23,55 +23,59 @@ class BasicBlock(Block):
block.dominated_by.append(self) block.dominated_by.append(self)
def find_leaders(statements): def find_leaders(statements, return_jump_targets=False):
"""Determine the leaders, which are: """
1. The first statement. - Determine the leaders, which are:
2. Any statement that is the target of a jump. 1. The first statement.
3. Any statement that follows directly follows a jump.""" 2. Any statement that is the target of a jump.
3. Any statement that follows directly follows a jump.
- To determine the leaders, a list of known jump targets is created. This
list can also be returned for later use.
"""
leaders = [0] leaders = [0]
jump_target_labels = [] jump_targets = []
# Append statements following jumps and save jump target labels # Append statements following jumps and save jump target labels
for i, statement in enumerate(statements[1:]): for i, statement in enumerate(statements[1:]):
if statement.is_jump(): if statement.is_jump():
leaders.append(i + 2) leaders.append(i + 2)
jump_target_labels.append(statement[-1]) jump_targets.append(statement[-1])
# Append jump targets # Append jump targets
for i, statement in enumerate(statements[1:]): for i, statement in enumerate(statements[1:]):
if i + 1 not in leaders \ if i + 1 not in leaders \
and statement.is_label() \ and statement.is_label() \
and statement.name in jump_target_labels: and statement.name in jump_targets:
leaders.append(i + 1) leaders.append(i + 1)
leaders.sort() leaders.sort()
return leaders return (leaders, jump_targets) if return_jump_targets else leaders
def find_basic_blocks(statements): def find_basic_blocks(statements, return_jump_targets=False):
"""Divide a statement list into basic blocks. Returns a list of basic """Divide a statement list into basic blocks. Returns a list of basic
blocks, which are also statement lists.""" blocks, which are also statement lists."""
leaders = find_leaders(statements) leaders, jump_targets = find_leaders(statements, True)
blocks = [] blocks = []
for i in range(len(leaders) - 1): for i in xrange(len(leaders) - 1):
blocks.append(BasicBlock(statements[leaders[i]:leaders[i + 1]])) blocks.append(BasicBlock(statements[leaders[i]:leaders[i + 1]]))
blocks.append(BasicBlock(statements[leaders[-1]:])) blocks.append(BasicBlock(statements[leaders[-1]:]))
# Add a target block for unknown jump targets # Add a target block for unknown jump targets
blocks.append(BasicBlock([], dummy=True)) #blocks.append(BasicBlock([], dummy=True))
return blocks return (blocks, jump_targets) if return_jump_targets else blocks
def generate_flow_graph(blocks): def generate_flow_graph(blocks):
"""Add flow graph edge administration of an ordered sequence of basic """Add flow graph edge administration of an ordered sequence of basic
blocks.""" blocks."""
dummy_block = blocks[-1] #dummy_block = blocks[-1]
for i, b in enumerate(blocks[:-1]): for i, b in enumerate(blocks):
last_statement = b[-1] last_statement = b[-1]
if last_statement.is_jump(): if last_statement.is_jump():
...@@ -81,15 +85,15 @@ def generate_flow_graph(blocks): ...@@ -81,15 +85,15 @@ def generate_flow_graph(blocks):
# label matches the jump target # label matches the jump target
target_found = False target_found = False
for other in blocks[:-1]: for other in blocks:
if other[0].is_label(target): if other[0].is_label(target):
b.add_edge_to(other) b.add_edge_to(other)
target_found = True target_found = True
# If the jump target is outside the program, add an edge to the # If the jump target is outside the program, add an edge to the
# dummy block # dummy block
if not target_found: #if not target_found:
b.add_edge_to(dummy_block) # b.add_edge_to(dummy_block)
# A branch and jump-and-line instruction also creates an edge to # A branch and jump-and-line instruction also creates an edge to
# the next block # the next block
......
...@@ -2,13 +2,22 @@ from copy import copy ...@@ -2,13 +2,22 @@ from copy import copy
RESERVED_REGISTERS = ['$fp', '$sp', '$31'] RESERVED_REGISTERS = ['$fp', '$sp', '$31']
RESERVED_USE = ['$%d' % i for i in range(2, 8)] \
+ ['$f%d' % i for i in range(32)]
RESERVED_DEF = ['$2', '$3']
def is_reg_dead_after(reg, block, index): def is_reg_dead_after(reg, block, index, known_jump_targets=[]):
"""Check if a register is dead after a certain point in a basic block.""" """Check if a register is dead after a certain point in a basic block."""
if reg in RESERVED_REGISTERS: if reg in RESERVED_REGISTERS:
return False return False
# If the block jumps to an unknown jump target, make sure that definitions
# of reserved argument registers are not removed
if reg in RESERVED_USE and block[-1].is_command('jal') \
and block[-1][0] not in known_jump_targets:
return False
if index < len(block) - 1: if index < len(block) - 1:
for s in block[index + 1:]: for s in block[index + 1:]:
# If used, the previous definition is live # If used, the previous definition is live
...@@ -25,16 +34,15 @@ def is_reg_dead_after(reg, block, index): ...@@ -25,16 +34,15 @@ def is_reg_dead_after(reg, block, index):
def create_use_def(block): def create_use_def(block):
#if block.dummy:
# block.use_set = set(RESERVED_USE)
# block.def_set = set(RESERVED_DEF)
# return
# Get the last of each definition series and put in in the `def' set
used = set() used = set()
defined = set() defined = set()
if block.dummy:
block.use_set = set(['$4', '$5', '$6', '$7', \
'$f0', '$f3', '$f4', '$f12', '$2'])
block.def_set = set(['$2', '$3'])
return
# Get the last of each definition series and put in in the `def' set
block.use_set = set() block.use_set = set()
block.def_set = set() block.def_set = set()
......
...@@ -15,14 +15,11 @@ def optimize(program, verbose=0): ...@@ -15,14 +15,11 @@ def optimize(program, verbose=0):
iterations = 0 iterations = 0
while changed: while changed:
<<<<<<< HEAD
iterations += 1 iterations += 1
if verbose > 1: if verbose > 1:
print 'main iteration %d', iterations print 'main iteration %d', iterations
=======
>>>>>>> 98c43ff02c474a62e42ac89ba9fe20be98f9eccd
changed = False changed = False
# Optimize on a global level # Optimize on a global level
......
...@@ -4,31 +4,32 @@ from src.statement import Statement as S ...@@ -4,31 +4,32 @@ from src.statement import Statement as S
from src.liveness import is_reg_dead_after from src.liveness import is_reg_dead_after
def reg_can_be_used_in(reg, block, start, end): #def reg_can_be_used_in(reg, block, start, end):
"""Check if a register addres safely be used in a block section using local # """Check if a register addres safely be used in a block section using local
dataflow analysis.""" # dataflow analysis."""
# Check if the register used or defined in the block section # # Check if the register used or defined in the block section
for s in block[start:end]: # for s in block[start:end]:
if s.uses(reg) or s.defines(reg): # if s.uses(reg) or s.defines(reg):
return False # return False
#
# Check if the register is used inside the block after the specified # # Check if the register is used inside the block after the specified
# section, without having been re-assigned first # # section, without having been re-assigned first
for s in block[end:]: # for s in block[end:]:
if s.uses(reg): # if s.uses(reg):
return False # return False
elif s.defines(reg): # elif s.defines(reg):
return True # return True
#
return reg not in block.live_out # return reg not in block.live_out
def find_free_reg(block, start, end): def find_free_reg(block, start):
"""Find a temporary register that is free in a given list of statements.""" """Find a temporary register that is free in a given list of statements."""
for i in xrange(8, 16): for i in xrange(8, 16):
tmp = '$%d' % i tmp = '$%d' % i
if reg_can_be_used_in(tmp, block, start, end): #if reg_can_be_used_in(tmp, block, start, end):
if is_reg_dead_after(tmp, block, start):
return tmp return tmp
raise Exception('No temporary register is available.') raise Exception('No temporary register is available.')
...@@ -78,7 +79,7 @@ def eliminate_common_subexpressions(block): ...@@ -78,7 +79,7 @@ def eliminate_common_subexpressions(block):
occurrences.append(block.pointer - 1) occurrences.append(block.pointer - 1)
if len(occurrences) > 1: if len(occurrences) > 1:
new_reg = find_free_reg(block, occurrences[0], occurrences[-1]) new_reg = find_free_reg(block, occurrences[0])
# Replace all occurrences with a move statement # Replace all occurrences with a move statement
message = 'Common subexpression reference: %s %s' \ message = 'Common subexpression reference: %s %s' \
...@@ -341,49 +342,6 @@ def copy_propagation(block): ...@@ -341,49 +342,6 @@ def copy_propagation(block):
return changed return changed
def algebraic_transformations(block):
"""
Change ineffective or useless algebraic expressions. Handled are:
- x = y + 0 -> x = y
- x = y - 0 -> x = y
- x = y * 1 -> x = y
- x = y * 0 -> x = 0
- x = y * 2 -> x = x << 1
"""
changed = False
block.reset()
while not block.end():
s = block.read()
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'):
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): def eliminate_dead_code(block):
""" """
Dead code elimination: Dead code elimination:
......
...@@ -92,7 +92,6 @@ class Program(Block): ...@@ -92,7 +92,6 @@ class Program(Block):
for b in self.blocks: for b in self.blocks:
b.verbose = self.verbose b.verbose = self.verbose
# Remove the old statement list, since it will probably change
del self.statements del self.statements
def perform_dataflow_analysis(self): def perform_dataflow_analysis(self):
......
...@@ -207,7 +207,7 @@ class Statement: ...@@ -207,7 +207,7 @@ class Statement:
if m: if m:
use.add(m.group(1)) use.add(m.group(1))
else: elif not re.match('^\$LC\d+$', self[1]):
use.add(self[1]) use.add(self[1])
# Case arg2 # Case arg2
......
import unittest import unittest
from src.statement import Statement as S from src.statement import Statement as S
from src.program import Program as P
from src.dataflow import BasicBlock as B, find_leaders, find_basic_blocks, \ from src.dataflow import BasicBlock as B, find_leaders, find_basic_blocks, \
generate_flow_graph generate_flow_graph
...@@ -20,11 +21,8 @@ class TestDataflow(unittest.TestCase): ...@@ -20,11 +21,8 @@ class TestDataflow(unittest.TestCase):
def test_find_basic_blocks(self): def test_find_basic_blocks(self):
s = self.statements s = self.statements
self.assertEqual( statements = map(lambda b: b.statements, find_basic_blocks(s))
map(lambda b: b.statements, find_basic_blocks(s)[:-1]), self.assertEqual(statements, [s[:2], s[2:4], s[4:]])
[B(s[:2]).statements, B(s[2:4]).statements,
B(s[4:]).statements]
)
def test_generate_flow_graph_simple(self): def test_generate_flow_graph_simple(self):
b1 = B([S('command', 'foo'), S('command', 'j', 'b2')]) b1 = B([S('command', 'foo'), S('command', 'j', 'b2')])
......
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