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

Fixed liveness analysis.

parent 770cbc9f
......@@ -23,55 +23,59 @@ class BasicBlock(Block):
block.dominated_by.append(self)
def find_leaders(statements):
"""Determine the leaders, which are:
1. The first statement.
2. Any statement that is the target of a jump.
3. Any statement that follows directly follows a jump."""
def find_leaders(statements, return_jump_targets=False):
"""
- Determine the leaders, which are:
1. The first statement.
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]
jump_target_labels = []
jump_targets = []
# Append statements following jumps and save jump target labels
for i, statement in enumerate(statements[1:]):
if statement.is_jump():
leaders.append(i + 2)
jump_target_labels.append(statement[-1])
jump_targets.append(statement[-1])
# Append jump targets
for i, statement in enumerate(statements[1:]):
if i + 1 not in leaders \
and statement.is_label() \
and statement.name in jump_target_labels:
and statement.name in jump_targets:
leaders.append(i + 1)
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
blocks, which are also statement lists."""
leaders = find_leaders(statements)
leaders, jump_targets = find_leaders(statements, True)
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[-1]:]))
# 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):
"""Add flow graph edge administration of an ordered sequence of basic
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]
if last_statement.is_jump():
......@@ -81,15 +85,15 @@ def generate_flow_graph(blocks):
# label matches the jump target
target_found = False
for other in blocks[:-1]:
for other in blocks:
if other[0].is_label(target):
b.add_edge_to(other)
target_found = True
# If the jump target is outside the program, add an edge to the
# dummy block
if not target_found:
b.add_edge_to(dummy_block)
#if not target_found:
# b.add_edge_to(dummy_block)
# A branch and jump-and-line instruction also creates an edge to
# the next block
......
......@@ -2,13 +2,22 @@ from copy import copy
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."""
if reg in RESERVED_REGISTERS:
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:
for s in block[index + 1:]:
# If used, the previous definition is live
......@@ -25,16 +34,15 @@ def is_reg_dead_after(reg, block, index):
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()
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.def_set = set()
......
......@@ -15,14 +15,11 @@ def optimize(program, verbose=0):
iterations = 0
while changed:
<<<<<<< HEAD
iterations += 1
if verbose > 1:
print 'main iteration %d', iterations
=======
>>>>>>> 98c43ff02c474a62e42ac89ba9fe20be98f9eccd
changed = False
# Optimize on a global level
......
......@@ -4,31 +4,32 @@ from src.statement import Statement as S
from src.liveness import is_reg_dead_after
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 reg not in block.live_out
def find_free_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
# 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 reg not in block.live_out
def find_free_reg(block, start):
"""Find a temporary register that is free in a given list of statements."""
for i in xrange(8, 16):
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
raise Exception('No temporary register is available.')
......@@ -78,7 +79,7 @@ def eliminate_common_subexpressions(block):
occurrences.append(block.pointer - 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
message = 'Common subexpression reference: %s %s' \
......@@ -341,49 +342,6 @@ def copy_propagation(block):
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):
"""
Dead code elimination:
......
......@@ -92,7 +92,6 @@ class Program(Block):
for b in self.blocks:
b.verbose = self.verbose
# Remove the old statement list, since it will probably change
del self.statements
def perform_dataflow_analysis(self):
......
......@@ -207,7 +207,7 @@ class Statement:
if m:
use.add(m.group(1))
else:
elif not re.match('^\$LC\d+$', self[1]):
use.add(self[1])
# Case arg2
......
import unittest
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, \
generate_flow_graph
......@@ -20,11 +21,8 @@ class TestDataflow(unittest.TestCase):
def test_find_basic_blocks(self):
s = self.statements
self.assertEqual(
map(lambda b: b.statements, find_basic_blocks(s)[:-1]),
[B(s[:2]).statements, B(s[2:4]).statements,
B(s[4:]).statements]
)
statements = map(lambda b: b.statements, find_basic_blocks(s))
self.assertEqual(statements, [s[:2], s[2:4], s[4:]])
def test_generate_flow_graph_simple(self):
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