Przeglądaj źródła

Added Program class for easy handling of statement lists (readability is massively increased).

Taddeus Kroes 14 lat temu
rodzic
commit
b45a5aedb2
6 zmienionych plików z 155 dodań i 83 usunięć
  1. 5 12
      main.py
  2. 12 68
      src/optimize/__init__.py
  3. 24 0
      src/optimize/redundancies.py
  4. 3 2
      src/parser.py
  5. 99 0
      src/program.py
  6. 12 1
      tests/test_optimize.py

+ 5 - 12
main.py

@@ -1,7 +1,6 @@
 #!/usr/bin/python
 from src.parser import parse_file
 from src.optimize import optimize
-from src.writer import write_statements
 
 if __name__ == '__main__':
     from sys import argv, exit
@@ -11,21 +10,15 @@ if __name__ == '__main__':
                 % argv[0]
         exit(1)
 
-    # Parse File
-    original = parse_file(argv[1])
+    # Parse file
+    program = parse_file(argv[1])
 
     if len(argv) > 3:
         # Save input assembly in new file for easy comparison
-        out = write_statements(original)
-        f = open(argv[3], 'w+')
-        f.write(out)
-        f.close()
+        program.save(argv[3])
 
-    optimized = optimize(original, verbose=1)
+    optimize(program, verbose=1)
 
     if len(argv) > 2:
         # Save output assembly
-        out = write_statements(optimized)
-        f = open(argv[2], 'w+')
-        f.write(out)
-        f.close()
+        program.save(argv[2])

+ 12 - 68
src/optimize/__init__.py

@@ -1,82 +1,28 @@
 from src.dataflow import find_basic_blocks, generate_flow_graph
-import src.liveness as liveness
-import src.reaching_definitions as reaching_definitions
-
-from redundancies import remove_redundant_jumps, move_1, move_2, move_3, \
-        move_4, load, shift, add
+from redundancies import remove_redundancies
 from advanced import eliminate_common_subexpressions, fold_constants, \
         copy_propagation, eliminate_dead_code
+import src.liveness as liveness
+import src.reaching_definitions as reaching_definitions
 
-
-def remove_redundancies(block):
-    """Execute all functions that remove redundant statements."""
-    callbacks = [move_1, move_2, move_3, move_4, load, shift, add]
-    old_len = -1
-    changed = False
-
-    while old_len != len(block):
-        old_len = len(block)
-
-        block.reset()
-
-        while not block.end():
-            s = block.read()
-
-            for callback in callbacks:
-                if callback(s, block):
-                    changed = True
-                    break
-
-    return changed
-
-
-def optimize_block(block):
-    """Optimize a basic block."""
-    #changed = True
-
-    #while changed:
-    #    changed = False
-
-    #    if remove_redundancies(block): changed = True
-    #    if eliminate_common_subexpressions(block): changed = True
-    #    if fold_constants(block): changed = True
-    #    if copy_propagation(block): changed = True
-    #    if eliminate_dead_code(block): changed = True
-    #    print 'iteration'
-
-    while remove_redundancies(block) \
-            | eliminate_common_subexpressions(block) \
-            | fold_constants(block) \
-            | copy_propagation(block) \
-            | eliminate_dead_code(block):
-            #| algebraic_transformations(block) \
-        #print 'iteration'
-        pass
-
-def optimize(statements, verbose=0):
+def optimize(program, verbose=0):
     """Optimization wrapper function, calls global and basic-block level
     optimization functions."""
-    # Optimize on a global level
-    # TODO: only count instructions (no directives)
-    o = len(statements)
-    remove_redundant_jumps(statements)
-    g = len(statements)
+    # Remember original number of statements
+    o = program.count_instructions()
 
-    # Divide into basic blocks
-    blocks = find_basic_blocks(statements)
+    # Optimize on a global level
+    program.optimize_global()
+    g = program.count_instructions()
 
     # Perform dataflow analysis
-    generate_flow_graph(blocks)
-    liveness.create_in_out(blocks)
-    reaching_definitions.create_in_out(blocks)
+    program.perform_dataflow_analysis()
 
     # Optimize basic blocks
-    map(optimize_block, blocks)
+    program.optimize_blocks()
 
     # Concatenate optimized blocks to obtain
-    block_statements = map(lambda b: b.statements, blocks)
-    opt_blocks = reduce(lambda a, b: a + b, block_statements)
-    b = len(opt_blocks)
+    b = program.count_instructions()
 
     # Print results
     if verbose:
@@ -85,5 +31,3 @@ def optimize(statements, verbose=0):
         print 'After basic block optimization: %d (%d removed)' % (b, g - b)
         print 'Statements removed:             %d (%d%%)' \
                 % (o - b, int((o - b) / float(b) * 100))
-
-    return opt_blocks

+ 24 - 0
src/optimize/redundancies.py

@@ -1,6 +1,28 @@
 import re
 
 
+def remove_redundancies(block):
+    """Execute all functions that remove redundant statements."""
+    callbacks = [move_1, move_2, move_3, move_4, load, shift, add]
+    old_len = -1
+    changed = False
+
+    while old_len != len(block):
+        old_len = len(block)
+
+        block.reset()
+
+        while not block.end():
+            s = block.read()
+
+            for callback in callbacks:
+                if callback(s, block):
+                    changed = True
+                    break
+
+    return changed
+
+
 def move_1(mov, statements):
     """
     mov $regA, $regA          ->  --- remove it
@@ -124,3 +146,5 @@ def remove_redundant_jumps(statements):
                         s.name = 'bne' if s.is_command('beq') else 'beq'
                         s[2] = j[0]
                         statements.replace(3, [s, label])
+
+    statements.reset()

+ 3 - 2
src/parser.py

@@ -1,7 +1,8 @@
 import ply.lex as lex
 import ply.yacc as yacc
 
-from statement import Statement as S, Block
+from statement import Statement as S
+from program import Program
 
 
 # Global statements administration
@@ -126,4 +127,4 @@ def parse_file(filename):
     except IOError:
         raise Exception('File "%s" could not be opened' % filename)
 
-    return Block(statements)
+    return Program(statements)

+ 99 - 0
src/program.py

@@ -0,0 +1,99 @@
+from statement import Block
+from dataflow import find_basic_blocks, generate_flow_graph
+from optimize.redundancies import remove_redundant_jumps, remove_redundancies
+from optimize.advanced import eliminate_common_subexpressions, \
+        fold_constants, copy_propagation, eliminate_dead_code
+from writer import write_statements
+import liveness
+import reaching_definitions
+
+
+class Program(Block):
+    def __init__(self, statements):
+        """Start with a given statement list."""
+        Block.__init__(self, statements)
+
+    def __iter__(self):
+        return iter(self.blocks)
+
+    def __len__(self):
+        """Get the number of statements in the program."""
+        return len(self.statements) if hasattr(self, 'statements') \
+               else reduce(lambda a, b: len(a) + len(b), self.blocks, 0)
+
+    def get_statements(self):
+        """Concatenate the statements of all blocks and return the resulting
+        list."""
+        if hasattr(self, 'statements'):
+            return self.statements
+        else:
+            return reduce(lambda a, b: a + b,
+                          [b.statements for b in self.blocks])
+
+    def count_instructions(self):
+        """Count the number of statements that are commands or labels."""
+        return len(filter(lambda s: s.is_command() or s.is_label(),
+                          self.get_statements()))
+
+    def optimize_global(self):
+        """Optimize on a global level."""
+        remove_redundant_jumps(self)
+
+    def optimize_blocks(self):
+        """Optimize on block level. Keep executing all optimizations until no
+        more changes occur."""
+        self.program_iterations = self.block_iterations = 0
+        program_changed = True
+
+        while program_changed:
+            self.program_iterations += 1
+            program_changed = False
+
+            for block in self.blocks:
+                self.block_iterations += 1
+                block_changed = True
+
+                while block_changed:
+                    block_changed = False
+
+                    if remove_redundancies(block):
+                        block_changed = True
+
+                    if eliminate_common_subexpressions(block):
+                        block_changed = True
+
+                    if fold_constants(block):
+                        block_changed = True
+
+                    if copy_propagation(block):
+                        block_changed = True
+
+                    if eliminate_dead_code(block):
+                        block_changed = True
+
+                    if block_changed:
+                        program_changed = True
+
+    def find_basic_blocks(self):
+        """Divide the statement list into basic blocks."""
+        self.blocks = find_basic_blocks(self.statements)
+
+        # Remove the old statment list, since it will probably change
+        del self.statements
+
+    def perform_dataflow_analysis(self):
+        """Perform dataflow analysis:
+           -Divide the statement list into basic blocks
+           - Generate flow graph
+           - Create liveness sets: def, use, in, out
+           - Create reaching definitions sets: gen, kill, in, out"""
+        self.find_basic_blocks()
+        generate_flow_graph(self.blocks)
+        liveness.create_in_out(self.blocks)
+        reaching_definitions.create_in_out(self.blocks)
+
+    def save(self, filename):
+        """Save the program in the specified file."""
+        f = open(filename, 'w+')
+        f.write(write_statements(self.get_statements()))
+        f.close()

+ 12 - 1
tests/test_optimize.py

@@ -1,10 +1,21 @@
 import unittest
 
 from src.optimize.redundancies import remove_redundant_jumps
-from src.optimize import optimize_block
+from src.program import Program
 from src.statement import Statement as S, Block as B
 
 
+def optimize_block(block):
+    """Optimize a basic block using a Program object."""
+    program = Program([])
+
+    program.blocks = [block]
+    del program.statements
+    program.optimize_blocks()
+
+    return program.blocks
+
+
 class TestOptimize(unittest.TestCase):
 
     def setUp(self):