changeset 312:2c9768114877

Added cool logging formatter
author Windel Bouwman
date Mon, 16 Dec 2013 17:58:15 +0100
parents ff665880a6b0
children 04cf4d26a3bc
files kernel/make.py python/ppci/__init__.py python/ppci/c3/codegenerator.py python/ppci/codegen/codegen.py python/ppci/codegen/graph.py python/ppci/codegen/registerallocator.py python/ppci/common.py python/ppci/errors.py python/ppci/ir.py python/ppci/irutils.py python/zcc.py test/testzcc.py
diffstat 12 files changed, 217 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/kernel/make.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/kernel/make.py	Mon Dec 16 17:58:15 2013 +0100
@@ -2,7 +2,6 @@
 
 import sys
 import os
-import zcc
 
 def make_kernel():
     arglist = ['memory.c3', 'kernel.c3', 'syscall.c3', 'process.c3']
@@ -16,4 +15,5 @@
 
 if __name__ == '__main__':
     sys.path.insert(0, os.path.join('..', 'python'))
+    import zcc
     make_kernel()
--- a/python/ppci/__init__.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/__init__.py	Mon Dec 16 17:58:15 2013 +0100
@@ -10,4 +10,4 @@
    sys.exit(1)
 
 from .common import SourceLocation, SourceRange, Token
-from .errors import CompilerError, DiagnosticsManager
+from .common import CompilerError, DiagnosticsManager
--- a/python/ppci/c3/codegenerator.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/c3/codegenerator.py	Mon Dec 16 17:58:15 2013 +0100
@@ -265,7 +265,7 @@
             if type(expr.val) in typemap:
                 expr.typ = self.pkg.scope[typemap[type(expr.val)]]
             else:
-                raise SemanticError('Unknown literal type {}'.format(expr.val))
+                raise SemanticError('Unknown literal type {}'.format(expr.val), expr.loc)
             return ir.Const(expr.val)
         elif type(expr) is ast.TypeCast:
             return self.gen_type_cast(expr)
--- a/python/ppci/codegen/codegen.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/codegen/codegen.py	Mon Dec 16 17:58:15 2013 +0100
@@ -1,8 +1,10 @@
 from ..ir import Module
+from ..irutils import Verifier
 from target import Target
 from ppci import CompilerError
 from .canon import make as canonicalize
 from .registerallocator import RegisterAllocator
+import logging
 
 
 class CodeGenerator:
@@ -11,21 +13,28 @@
         # TODO: schedule traces in better order.
         # This is optional!
         assert isinstance(target, Target), target
+        self.logger = logging.getLogger('codegen')
         self.target = target
         self.ins_sel = target.ins_sel
         self.ra = RegisterAllocator()
+        self.verifier = Verifier()
 
     def generateFunc(self, irfunc, outs):
         """ Generate code for one function into a frame """
+        self.logger.info('Generating code for {}'.format(irfunc.name))
         # Create a frame for this function:
         frame = self.target.FrameClass(irfunc.name)
 
         # Canonicalize the intermediate language:
         canonicalize(irfunc, frame)
+        self.logger.info('after canonicalize', extra={'irfunc':irfunc})
+        self.verifier.verify_function(irfunc)
         self.ins_sel.munchFunction(irfunc, frame)
+        self.logger.info('Selected instructions', extra={'ppci_frame':frame})
 
         # Do register allocation:
         self.ra.allocFrame(frame)
+        self.logger.info('Registers allocated, now adding final glue')
         # TODO: Peep-hole here?
 
         # Add label and return and stack adjustment:
@@ -34,6 +43,7 @@
         # Materialize the register allocated instructions into a stream of
         # real instructions.
         frame.lower_to(outs)
+        self.logger.info('Instructions materialized')
         return frame
 
     def generate(self, ircode, outs):
--- a/python/ppci/codegen/graph.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/codegen/graph.py	Mon Dec 16 17:58:15 2013 +0100
@@ -34,9 +34,9 @@
     def to_dot(self, f):
         """ Generate graphviz dot representation """
         for n in self.nodes:
-            print('{} [label="{}" shape=box3d];'.format(id(n), n), file=f)
+            print('  {} [label="{}" shape=box3d];'.format(id(n), n), file=f)
         for n, m in self.edges:
-            print('{} -> {};'.format(id(n), id(m)), file=f)
+            print('  {} -> {};'.format(id(n), id(m)), file=f)
 
 
 class Node:
--- a/python/ppci/codegen/registerallocator.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/codegen/registerallocator.py	Mon Dec 16 17:58:15 2013 +0100
@@ -1,3 +1,4 @@
+import logging
 from .flowgraph import FlowGraph
 from .interferencegraph import InterferenceGraph
 
@@ -21,6 +22,8 @@
         - (optional) spill registers
         - select registers
     """
+    def __init__(self):
+        self.logger = logging.getLogger('registerallocator')
 
     def InitData(self, f):
         self.f = f
@@ -38,7 +41,9 @@
     def Build(self):
         """ 1. Construct interference graph from instruction list """
         self.f.cfg = FlowGraph(self.f.instructions)
+        self.logger.info('Constructed flowgraph', extra={'ra_cfg':self.f.cfg})
         self.f.ig = InterferenceGraph(self.f.cfg)
+        self.logger.info('Constructed interferencegraph', extra={'ra_ig':self.f.ig})
 
         self.Node = self.f.ig.getNode
 
@@ -146,14 +151,12 @@
             v = m.src[0] if u is m.dst[0] else m.dst[0]
 
     def SelectSpill(self):
-        # TODO
-        pass
+        raise NotImplementedError("Spill is not implemented")
 
     def AssignColors(self):
         """ Add nodes back to the graph to color it. """
-        # Add nodes back to the graph:
         while self.selectStack:
-            n = self.selectStack.pop(-1)
+            n = self.selectStack.pop(-1)  # Start with the last added
             self.f.ig.addNode(n)
             takenregs = set(self.color[m] for m in n.Adjecent)
             okColors = self.regs - takenregs
@@ -192,4 +195,3 @@
                 break # Done!
         self.AssignColors()
         self.ApplyColors()
-
--- a/python/ppci/common.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/common.py	Mon Dec 16 17:58:15 2013 +0100
@@ -1,5 +1,11 @@
 from collections import namedtuple
+import logging
 
+"""
+   Error handling routines
+   Diagnostic utils
+   Source location structures
+"""
 
 # Token is used in the lexical analyzer:
 class Token:
@@ -27,3 +33,79 @@
 
 
 SourceRange = namedtuple('SourceRange', ['p1', 'p2'])
+
+
+class CompilerError(Exception):
+    def __init__(self, msg, loc=None):
+        self.msg = msg
+        self.loc = loc
+        if loc:
+            assert type(loc) is SourceLocation, \
+                   '{0} must be SourceLocation'.format(type(loc))
+            self.row = loc.row
+            self.col = loc.col
+        else:
+            self.row = self.col = 0
+
+    def __repr__(self):
+        return '"{}"'.format(self.msg)
+
+
+class DiagnosticsManager:
+    def __init__(self):
+        self.diags = []
+        self.sources = {}
+        self.logger = logging.getLogger('diagnostics')
+
+    def addSource(self, name, src):
+        self.logger.info('Adding source {}'.format(name))
+        self.sources[name] = src
+
+    def addDiag(self, d):
+        #self.logger.warning(str(d.msg))
+        self.diags.append(d)
+
+    def error(self, msg, loc):
+        self.addDiag(CompilerError(msg, loc))
+
+    def clear(self):
+        del self.diags[:]
+        self.sources.clear()
+
+    def printErrors(self):
+        if len(self.diags) > 0:
+            print('{0} Errors'.format(len(self.diags)))
+            for d in self.diags:
+                self.printError(d)
+
+    def printError(self, e):
+        def printLine(row, txt):
+            print(str(row)+':'+txt)
+        print('==============')
+        if not e.loc:
+            print('Error: {0}'.format(e))
+        else:
+            if e.loc.filename not in self.sources:
+                print('Error: {0}'.format(e))
+                return
+            print("File: {}".format(e.loc.filename))
+            source = self.sources[e.loc.filename]
+            lines = source.split('\n')
+            ro, co = e.row, e.col
+            prerow = ro - 2
+            if prerow < 1:
+               prerow = 1
+            afterrow = ro + 3
+            if afterrow > len(lines):
+               afterrow = len(lines)
+
+            # print preceding source lines:
+            for r in range(prerow, ro):
+               printLine(r, lines[r-1])
+            # print source line containing error:
+            printLine(ro, lines[ro-1])
+            print(' '*(len(str(ro)+':')+co-1) + '^ Error: {0}'.format(e.msg))
+            # print trailing source line:
+            for r in range(ro+1, afterrow+1):
+              printLine(r, lines[r-1])
+        print('==============')
--- a/python/ppci/errors.py	Mon Dec 16 12:49:24 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-"""
-   Error handling routines
-   Diagnostic utils
-"""
-
-import logging
-from . import SourceLocation
-
-
-class CompilerError(Exception):
-    def __init__(self, msg, loc=None):
-        self.msg = msg
-        self.loc = loc
-        if loc:
-            assert type(loc) is SourceLocation, \
-                   '{0} must be SourceLocation'.format(type(loc))
-            self.row = loc.row
-            self.col = loc.col
-        else:
-            self.row = self.col = 0
-
-    def __repr__(self):
-        return '"{}"'.format(self.msg)
-
-
-class DiagnosticsManager:
-    def __init__(self):
-        self.diags = []
-        self.sources = {}
-        self.logger = logging.getLogger('diagnostics')
-
-    def addSource(self, name, src):
-        self.logger.info('Adding source {}'.format(name))
-        self.sources[name] = src
-
-    def addDiag(self, d):
-        #self.logger.warning(str(d.msg))
-        self.diags.append(d)
-
-    def error(self, msg, loc):
-        self.addDiag(CompilerError(msg, loc))
-
-    def clear(self):
-        del self.diags[:]
-        self.sources.clear()
-
-    def printErrors(self):
-        if len(self.diags) > 0:
-            print('{0} Errors'.format(len(self.diags)))
-            for d in self.diags:
-                self.printError(d)
-
-    def printError(self, e):
-        def printLine(row, txt):
-            print(str(row)+':'+txt)
-        print('==============')
-        if not e.loc:
-            print('Error: {0}'.format(e))
-        else:
-            if e.loc.filename not in self.sources:
-                print('Error: {0}'.format(e))
-                return
-            print("File: {}".format(e.loc.filename))
-            source = self.sources[e.loc.filename]
-            lines = source.split('\n')
-            ro, co = e.row, e.col
-            prerow = ro - 2
-            if prerow < 1:
-               prerow = 1
-            afterrow = ro + 3
-            if afterrow > len(lines):
-               afterrow = len(lines)
-
-            # print preceding source lines:
-            for r in range(prerow, ro):
-               printLine(r, lines[r-1])
-            # print source line containing error:
-            printLine(ro, lines[ro-1])
-            print(' '*(len(str(ro)+':')+co-1) + '^ Error: {0}'.format(e.msg))
-            # print trailing source line:
-            for r in range(ro+1, afterrow+1):
-              printLine(r, lines[r-1])
-        print('==============')
--- a/python/ppci/ir.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/ir.py	Mon Dec 16 17:58:15 2013 +0100
@@ -166,6 +166,7 @@
         if not self.Empty:
             return self.LastInstruction.Targets
         return []
+
     Successors = property(getSuccessors)
 
     def getPredecessors(self):
@@ -174,6 +175,7 @@
             if self in bb.Successors:
                 preds.append(bb)
         return preds
+
     Predecessors = property(getPredecessors)
 
     def precedes(self, other):
@@ -326,7 +328,9 @@
 
 class Statement:
     """ Base class for all instructions. """
-    pass
+    @property
+    def IsTerminator(self):
+        return isinstance(self, LastStatement)
 
 
 class Move(Statement):
--- a/python/ppci/irutils.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/ppci/irutils.py	Mon Dec 16 17:58:15 2013 +0100
@@ -3,7 +3,6 @@
     Some utilities for ir-code.
 """
 import re
-from .ir import Temp, Block, Function, Statement
 from . import ir
 
 def dumpgv(m, outf):
@@ -25,18 +24,24 @@
 
 
 class Writer:
+    def __init__(self, extra_indent=''):
+        self.extra_indent = extra_indent
+
     def write(self, ir, f):
         """ Write ir-code to file f """
-        print(ir, file=f)
+        print('{}{}'.format(self.extra_indent, ir), file=f)
         for v in ir.Variables:
-            print(str(v), file=f)
-        for fn in ir.Functions:
-            args = ','.join('i32 ' + str(a) for a in fn.arguments)
-            print('function i32 {}({})'.format(fn.name, args), file=f)
-            for bb in fn.Blocks:
-                print(' ' + str(bb), file=f)
-                for ins in bb.Instructions:
-                    print('  ' + str(ins), file=f)
+            print('{}{}'.format(self.extra_indent, v), file=f)
+        for function in ir.Functions:
+            self.write_function(function, f)
+
+    def write_function(self, fn, f):
+        args = ','.join('i32 ' + str(a) for a in fn.arguments)
+        print('{}function i32 {}({})'.format(self.extra_indent, fn.name, args), file=f)
+        for bb in fn.Blocks:
+            print('{} {}'.format(self.extra_indent, bb), file=f)
+            for ins in bb.Instructions:
+                print('{}  {}'.format(self.extra_indent, ins), file=f)
 
 
 class IrParseException(Exception):
@@ -194,8 +199,8 @@
         self.prepare()
 
     def prepare(self):
-        self.newTemp = NamedClassGenerator('reg', Temp).gen
-        self.newBlock2 = NamedClassGenerator('block', Block).gen
+        self.newTemp = NamedClassGenerator('reg', ir.Temp).gen
+        self.newBlock2 = NamedClassGenerator('block', ir.Block).gen
         self.bb = None
         self.m = None
         self.fn = None
@@ -206,7 +211,7 @@
         self.m = m
 
     def newFunction(self, name):
-        f = Function(name)
+        f = ir.Function(name)
         self.m.add_function(f)
         return f
 
@@ -227,8 +232,35 @@
         self.loc = l
 
     def emit(self, i):
-        assert isinstance(i, Statement)
+        assert isinstance(i, ir.Statement)
         i.debugLoc = self.loc
         if not self.bb:
             raise Exception('No basic block')
         self.bb.addInstruction(i)
+
+
+class Verifier:
+    def verify(self, module):
+        """ Verifies a module for some sanity """
+        assert isinstance(module, ir.Module)
+        for f in module.Functions:
+            self.verify_function(f)
+
+    def verify_function(self, function):
+        for b in function.Blocks:
+            self.verify_block_termination(b)
+
+        # Now we can build a dominator tree
+        for b in function.Blocks:
+            self.verify_block(b)
+
+    def verify_block_termination(self, block):
+        assert not block.Empty
+        assert block.LastInstruction.IsTerminator
+        
+    def verify_block(self, block):
+        for instruction in block.Instructions:
+            self.verify_instruction(instruction)
+
+    def verify_instruction(self, instruction):
+        pass
--- a/python/zcc.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/python/zcc.py	Mon Dec 16 17:58:15 2013 +0100
@@ -6,6 +6,7 @@
 
 from ppci.c3 import Builder
 import ppci
+from ppci.irutils import Verifier, Writer
 from ppci.codegen import CodeGenerator
 import outstream
 from utils import HexFile
@@ -18,12 +19,64 @@
 
 
 def logLevel(s):
+    """ Converts a string to a valid logging level """
     numeric_level = getattr(logging, s.upper(), None)
     if not isinstance(numeric_level, int):
         raise ValueError('Invalid log level: {}'.format(s))
     return numeric_level
 
 
+class RstFormatter(logging.Formatter):
+    """ Formatter that tries to create an rst document """
+    def __init__(self):
+        super().__init__(fmt=logformat)
+
+    def format(self, record):
+        s = super().format(record)
+        if hasattr(record, 'ircode'):
+            f = io.StringIO()
+            print('', file=f)
+            print('', file=f)
+            print('.. code::', file=f)
+            print('', file=f)
+            Writer('  ').write(record.ircode, f)
+            print('', file=f)
+            s += '\n' + f.getvalue()
+        if hasattr(record, 'irfunc'):
+            f = io.StringIO()
+            print('', file=f)
+            print('', file=f)
+            print('.. code::', file=f)
+            print('', file=f)
+            Writer('  ').write_function(record.irfunc, f)
+            print('', file=f)
+            s += '\n' + f.getvalue()
+        if hasattr(record, 'ppci_frame'):
+            f = io.StringIO()
+            frame = record.ppci_frame
+            print('', file=f)
+            print('.. code::', file=f)
+            print('', file=f)
+            print('  {}'.format(frame.name), file=f)
+            for i in frame.instructions:
+                print('   {}'.format(i),file=f)
+            print('', file=f)
+            s += '\n' + f.getvalue()
+        if hasattr(record, 'ra_cfg'):
+            f = io.StringIO()
+            print('', file=f)
+            print('', file=f)
+            print('.. graphviz::', file=f)
+            print('', file=f)
+            print('  digraph G {', file=f)
+            cfg = record.ra_cfg
+            cfg.to_dot(f)
+            print('  }', file=f)
+            print('', file=f)
+            s += '\n' + f.getvalue()
+        return s
+
+
 target_list = [target.armtarget]
 targets = {t.name: t for t in target_list}
 targetnames = list(targets.keys())
@@ -67,6 +120,7 @@
         if not ircode:
             return
         # Optimization passes, TODO
+        Verifier().verify(ircode)
 
         if dumpir:
             f = io.StringIO()
@@ -74,7 +128,8 @@
             print(f.getvalue())
 
         # Code generation:
-        logging.info('Starting code generation for {}'.format(ircode))
+        d = {'ircode':ircode}
+        logging.info('Starting code generation for {}'.format(ircode), extra=d)
         cg.generate(ircode, outs)
     # TODO: fixup references, do this in another way?
     outs.backpatch()
@@ -84,6 +139,11 @@
 
 def main(args):
     logging.basicConfig(format=logformat, level=args.log)
+    #logging.getLogger().addHandler(RstLogHandler())
+    fh = logging.FileHandler('log.rst', mode='w')
+    fh.setFormatter(RstFormatter())
+    logging.getLogger().addHandler(fh)
+
     tg = targets[args.target]
     diag = ppci.DiagnosticsManager()
     outs = outstream.TextOutputStream()
--- a/test/testzcc.py	Mon Dec 16 12:49:24 2013 +0100
+++ b/test/testzcc.py	Mon Dec 16 17:58:15 2013 +0100
@@ -80,4 +80,4 @@
 
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main()
\ No newline at end of file