view python/ppci/irutils.py @ 317:e30a77ae359b

Added glue blocks
author Windel Bouwman
date Sun, 22 Dec 2013 15:50:59 +0100
parents 2c9768114877
children e84047f29c78
line wrap: on
line source


"""
    Some utilities for ir-code.
"""
import re
from . import ir

def dumpgv(m, outf):
    print('digraph G ', file=outf)
    print('{', file=outf)
    for f in m.Functions:
        print('{} [label="{}" shape=box3d]'.format(id(f), f), file=outf)
        for bb in f.Blocks:
            contents = str(bb) + '\n'
            contents += '\n'.join([str(i) for i in bb.Instructions])
            print('{0} [shape=note label="{1}"];'
                  .format(id(bb), contents), file=outf)
            for successor in bb.Successors:
                print('"{}" -> "{}"'.format(id(bb), id(successor)), file=outf)

        print('"{}" -> "{}" [label="entry"]'
              .format(id(f), id(f.entry)), file=outf)
    print('}', file=outf)


class Writer:
    def __init__(self, extra_indent=''):
        self.extra_indent = extra_indent

    def write(self, ir, f):
        """ Write ir-code to file f """
        print('{}{}'.format(self.extra_indent, ir), file=f)
        for v in ir.Variables:
            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):
    pass


class Reader:
    def read(self, f):
        """ Read ir code from file f """
        # Read lines from the file:
        lines = [line.rstrip() for line in f]

        # Create a regular expression for the lexing part:
        tok_spec = [
           ('NUMBER', r'\d+'),
           ('ID', r'[A-Za-z][A-Za-z\d_]*'),
           ('SKIP2', r'  '),
           ('SKIP1', r' '),
           ('OTHER', r'[\.,=:;\-+*\[\]/\(\)]|>|<|{|}|&|\^|\|')
            ]
        tok_re = '|'.join('(?P<%s>%s)' % pair for pair in tok_spec)
        gettok = re.compile(tok_re).match

        def tokenize():
            for line in lines:
                if not line:
                    continue  # Skip empty lines
                mo = gettok(line)
                first = True
                while mo:
                    typ = mo.lastgroup
                    val = mo.group(typ)
                    if typ == 'ID':
                        if val in ['function', 'module']:
                            typ = val
                        yield (typ, val)
                    elif typ == 'OTHER':
                        typ = val
                        yield (typ, val)
                    elif typ in ['SKIP1', 'SKIP2']:
                        if first:
                            yield (typ, val)
                    elif typ == 'NUMBER':
                        yield (typ, int(val))
                    else:
                        raise NotImplementedError(str(typ))
                    first = False
                    pos = mo.end()
                    mo = gettok(line, pos)
                if len(line) != pos:
                    raise IrParseException('Lex fault')
                yield ('eol', 'eol')
            yield ('eof', 'eof')
        self.tokens = tokenize()
        self.token = self.tokens.__next__()

        try:
            module = self.parse_module()
            return module
        except IrParseException as e:
            print(e)

    def next_token(self):
        t = self.token
        if t[0] != 'eof':
            self.token = self.tokens.__next__()
        return t

    @property
    def Peak(self):
        return self.token[0]

    def Consume(self, typ):
        if self.Peak == typ:
            return self.next_token()
        else:
            raise IrParseException('Expected "{}" got "{}"'.format(typ, self.Peak))
        
    def parse_module(self):
        """ Entry for recursive descent parser """
        self.Consume('module')
        name = self.Consume('ID')[1]
        module = ir.Module(name)
        self.Consume('eol')
        while self.Peak != 'eof':
            if self.Peak == 'function':
                module.add_function(self.parse_function())
            else:
                raise IrParseException('Expected function got {}'.format(self.Peak))
        return module
                
    def parse_function(self):
        self.Consume('function')
        self.parse_type()
        name = self.Consume('ID')[1]
        function = ir.Function(name)
        self.Consume('(')
        while self.Peak != ')':
            self.parse_type()
            self.Consume('ID')
            if self.Peak != ',':
                break
            else:
                self.Consume(',')
        self.Consume(')')
        self.Consume('eol')
        while self.Peak == 'SKIP1':
            function.add_block(self.parse_block())
        return function

    def parse_type(self):
        self.Consume('ID')

    def parse_block(self):
        self.Consume('SKIP1')
        name = self.Consume('ID')[1]
        block = ir.Block(name)
        self.Consume(':')
        self.Consume('eol')
        while self.Peak == 'SKIP2':
            self.parse_statement()
        return block

    def parse_statement(self):
        self.Consume('SKIP2')
        while self.Peak != 'eol':
            # raise NotImplementedError()
            self.next_token()
        self.Consume('eol')


# Constructing IR:

class NamedClassGenerator:
    def __init__(self, prefix, cls):
        self.prefix = prefix
        self.cls = cls

        def NumGen():
            a = 0
            while True:
                yield a
                a = a + 1
        self.nums = NumGen()

    def gen(self, prefix=None):
        if not prefix:
            prefix = self.prefix
        return self.cls('{0}{1}'.format(prefix, self.nums.__next__()))


class Builder:
    """ Base class for ir code generators """
    def __init__(self):
        self.prepare()

    def prepare(self):
        self.newTemp = NamedClassGenerator('reg', ir.Temp).gen
        self.newBlock2 = NamedClassGenerator('block', ir.Block).gen
        self.bb = None
        self.m = None
        self.fn = None
        self.loc = None

    # Helpers:
    def setModule(self, m):
        self.m = m

    def newFunction(self, name):
        f = ir.Function(name)
        self.m.add_function(f)
        return f

    def newBlock(self):
        assert self.fn
        b = self.newBlock2()
        b.function = self.fn
        return b

    def setFunction(self, f):
        self.fn = f
        self.bb = f.entry if f else None

    def setBlock(self, b):
        self.bb = b

    def setLoc(self, l):
        self.loc = l

    def emit(self, i):
        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
        for i in block.Instructions[:-1]:
            assert not isinstance(i, ir.LastStatement)
        
    def verify_block(self, block):
        for instruction in block.Instructions:
            self.verify_instruction(instruction)

    def verify_instruction(self, instruction):
        pass