Forráskód Böngészése

Fixed liveness analysis.

Taddeus Kroes 14 éve
szülő
commit
7018b528db
7 módosított fájl, 66 hozzáadás és 102 törlés
  1. 23 19
      src/dataflow.py
  2. 16 8
      src/liveness.py
  3. 0 3
      src/optimize/__init__.py
  4. 23 65
      src/optimize/advanced.py
  5. 0 1
      src/program.py
  6. 1 1
      src/statement.py
  7. 3 5
      tests/test_dataflow.py

+ 23 - 19
src/dataflow.py

@@ -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

+ 16 - 8
src/liveness.py

@@ -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()
 

+ 0 - 3
src/optimize/__init__.py

@@ -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

+ 23 - 65
src/optimize/advanced.py

@@ -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:

+ 0 - 1
src/program.py

@@ -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):

+ 1 - 1
src/statement.py

@@ -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

+ 3 - 5
tests/test_dataflow.py

@@ -1,6 +1,7 @@
 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')])