diff python/ppci/ir.py @ 301:6753763d3bec

merge codegen into ppci package
author Windel Bouwman
date Thu, 05 Dec 2013 17:02:38 +0100
parents python/ir.py@534b94b40aa8
children be7f60545368
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/ppci/ir.py	Thu Dec 05 17:02:38 2013 +0100
@@ -0,0 +1,477 @@
+"""
+Intermediate representation (IR) code classes.
+"""
+
+
+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])
+        outf.write('{0} [shape=note label="{1}"];\n'.format(id(bb), contents))
+        for successor in bb.Successors:
+           print('"{}" -> "{}"\n'.format(id(bb), id(successor)), file=outf)
+
+     outf.write('"{}" -> "{}" [label="entry"]\n'.format(id(f), id(f.entry)))
+    print('}', file=outf)
+
+
+class Module:
+    """ Main container of variables and functions. """
+    def __init__(self, name):
+        self.name = name
+        self.funcs = []
+        self.variables = []
+
+    def __repr__(self):
+        return 'IR-module [{0}]'.format(self.name)
+
+    def addFunc(self, f):
+        self.funcs.append(f)
+
+    addFunction = addFunc
+
+    def addVariable(self, v):
+        self.variables.append(v)
+
+    def getVariables(self):
+        return self.variables
+
+    Variables = property(getVariables)
+
+    def getFunctions(self):
+        return self.funcs
+
+    Functions = property(getFunctions)
+
+    def findFunction(self, name):
+        for f in self.funcs:
+            if f.name == name:
+                return f
+        raise KeyError(name)
+
+    getFunction = findFunction
+
+    def dump(self, indent='   '):
+        print(self)
+        for v in self.Variables:
+            print(indent, v)
+        for fn in self.Functions:
+            fn.dump(indent=indent+'   ')
+
+    # Analysis functions:
+    def check(self):
+        """ Perform sanity check on module """
+        for f in self.Functions:
+            f.check()
+
+
+class Function:
+    """
+      Function definition. Contains an entry block.
+    """
+    def __init__(self, name):
+        self.name = name
+        self.entry = Block('{}_entry'.format(name))
+        self.entry.function = self
+        self.epiloog = Block('{}_epilog'.format(name))
+        self.epiloog.function = self
+        self.epiloog.addInstruction(Terminator())
+        self.return_value = Temp('{}_retval'.format(name))
+        self.arguments = []
+        self.localvars = []
+
+    def __repr__(self):
+        args = ','.join(str(a) for a in self.arguments)
+        return 'Function {}({})'.format(self.name, args)
+
+    def addBlock(self, bb):
+        self.bbs.append(bb)
+        bb.function = self
+
+    def removeBlock(self, bb):
+        #self.bbs.remove(bb)
+        bb.function = None
+
+    def getBlocks(self):
+        bbs = [self.entry]
+        worklist = [self.entry]
+        while worklist:
+            b = worklist.pop()
+            for sb in b.Successors:
+                if sb not in bbs:
+                    bbs.append(sb)
+                    worklist.append(sb)
+        bbs.remove(self.entry)
+        if self.epiloog in bbs:
+            bbs.remove(self.epiloog)
+        bbs.insert(0, self.entry)
+        bbs.append(self.epiloog)
+        return bbs
+
+    def findBasicBlock(self, name):
+        for bb in self.bbs:
+            if bb.name == name:
+                return bb
+        raise KeyError(name)
+
+    Blocks = property(getBlocks)
+
+    @property
+    def Entry(self):
+        return self.entry
+
+    def check(self):
+        for b in self.Blocks:
+            b.check()
+
+    def addParameter(self, p):
+        assert type(p) is Parameter
+        p.num = len(self.arguments)
+        self.arguments.append(p)
+
+    def addLocal(self, l):
+        assert type(l) is LocalVariable
+        self.localvars.append(l)
+
+    def dump(self, indent=''):
+        print(indent+str(self))
+        for bb in self.Blocks:
+            print(indent+'   '+str(bb))
+            for ins in bb.Instructions:
+                print(indent +'   '*2 + str(ins))
+
+
+class Block:
+    """ 
+        Uninterrupted sequence of instructions with a label at the start.
+    """
+    def __init__(self, name):
+        self.name = name
+        self.function = None
+        self.instructions = []
+
+    parent = property(lambda s: s.function)
+
+    def __repr__(self):
+        return 'Block {0}'.format(self.name)
+
+    def addInstruction(self, i):
+        i.parent = self
+        assert not isinstance(self.LastInstruction, LastStatement)
+        self.instructions.append(i)
+
+    def replaceInstruction(self, i1, i2):
+        idx = self.instructions.index(i1)
+        i1.parent = None
+        i1.delete()
+        i2.parent = self
+        self.instructions[idx] = i2
+
+    def removeInstruction(self, i):
+        i.parent = None
+        i.delete()
+        self.instructions.remove(i)
+
+    @property
+    def Instructions(self):
+        return self.instructions
+
+    @property
+    def LastInstruction(self):
+        if not self.Empty:
+            return self.instructions[-1]
+
+    @property
+    def Empty(self):
+        return len(self.instructions) == 0
+
+    @property
+    def FirstInstruction(self):
+        return self.instructions[0]
+
+    def getSuccessors(self):
+        if not self.Empty:
+            return self.LastInstruction.Targets
+        return []
+    Successors = property(getSuccessors)
+
+    def getPredecessors(self):
+        preds = []
+        for bb in self.parent.Blocks:
+            if self in bb.Successors:
+                preds.append(bb)
+        return preds
+    Predecessors = property(getPredecessors)
+
+    def precedes(self, other):
+        raise NotImplementedError()
+
+    def check(self):
+        assert isinstance(self.LastInstruction, LastStatement)
+        for i in self.instructions[:-1]:
+            assert not isinstance(i, LastStatement)
+
+
+# Instructions:
+class Term:
+    def __init__(self, x):
+        self.x = x
+
+def match_tree(tree, pattern):
+    if type(pattern) is Term:
+        return True, {pattern: tree}
+    elif type(pattern) is Binop and type(tree) is Binop and tree.operation == pattern.operation:
+        res_a, mp_a = match_tree(tree.a, pattern.a)
+        res_b, mp_b = match_tree(tree.b, pattern.b)
+        assert not (mp_a.keys() & mp_b.keys())
+        mp_a.update(mp_b)
+        return res_a and res_b, mp_a
+    elif type(pattern) is Const and type(tree) is Const and pattern.value == tree.value:
+        return True, {}
+    else:
+        return False, {}
+
+
+class Expression:
+    pass
+
+
+class Const(Expression):
+    def __init__(self, value):
+        self.value = value
+
+    def __repr__(self):
+        return 'Const {}'.format(self.value)
+
+
+# Function calling:
+class Call(Expression):
+    def __init__(self, f, arguments):
+        self.f = f
+        assert type(f) is Function
+        self.arguments = arguments
+
+    def __repr__(self):
+        args = ', '.join([str(arg) for arg in self.arguments])
+        return '{}({})'.format(self.f.name, args)
+
+
+# Data operations
+class Binop(Expression):
+    ops = ['+', '-', '*', '/', '|', '&', '<<', '>>']
+    def __init__(self, value1, operation, value2):
+        assert operation in Binop.ops
+        self.a = value1
+        self.b = value2
+        self.operation = operation
+
+    def __repr__(self):
+        a, b = self.a, self.b
+        return '({} {} {})'.format(a, self.operation, b)
+
+
+def Add(a, b):
+    """ Convenience call """
+    return Binop(a, '+', b)
+
+
+def Sub(a, b):
+    return Binop(a, '-', b)
+
+
+def Mul(a, b):
+    return Binop(a, '*', b)
+
+
+def Div(a, b):
+    return Binop(a, '/', b)
+
+class Eseq(Expression):
+    """ Sequence of instructions where the last is an expression """
+    def __init__(self, stmt, e):
+        self.stmt = stmt
+        self.e = e
+
+    def __repr__(self):
+        return '({}, {})'.format(self.stmt, self.e)
+
+
+class Alloc(Expression):
+    """ Allocates space on the stack """
+    def __init__(self):
+        super().__init__()
+
+    def __repr__(self):
+        return 'Alloc'
+
+
+class Variable(Expression):
+    def __init__(self, name):
+        self.name = name
+
+    def __repr__(self):
+        return 'Var {}'.format(self.name)
+
+
+class LocalVariable(Variable):
+    def __repr__(self):
+        return 'Local {}'.format(self.name)
+
+
+class Parameter(Variable):
+    def __repr__(self):
+        return 'Param {}'.format(self.name)
+
+
+class Temp(Expression):
+    """ Temporary storage, same as register """
+    def __init__(self, name):
+        self.name = name
+
+    def __repr__(self):
+        return 'TMP_{}'.format(self.name)
+
+
+class Mem(Expression):
+    def __init__(self, e):
+        self.e = e
+
+    def __repr__(self):
+        return '[{}]'.format(self.e)
+
+
+class Statement:
+    """ Base class for all instructions. """
+    pass
+
+
+class Move(Statement):
+    def __init__(self, dst, src):
+        self.dst = dst
+        self.src = src
+
+    def __repr__(self):
+        return '{} = {}'.format(self.dst, self.src)
+
+
+class Exp(Statement):
+    def __init__(self, e):
+        self.e = e
+
+    def __repr__(self):
+        return '{}'.format(self.e)
+
+
+# Branching:
+class LastStatement(Statement):
+    def changeTarget(self, old, new):
+        idx = self.Targets.index(old)
+        self.Targets[idx] = new
+
+
+class Terminator(LastStatement):
+    """ Instruction that terminates the terminal block """
+    def __init__(self):
+        self.Targets = []
+
+    def __repr__(self):
+        return 'Terminator'
+
+
+class Jump(LastStatement):
+    def __init__(self, target):
+        self.Targets = [target]
+
+    def setTarget(self, t):
+        self.Targets[0] = t
+    target = property(lambda s: s.Targets[0], setTarget)
+
+    def __repr__(self):
+        return 'JUMP {}'.format(self.target.name)
+
+
+class CJump(LastStatement):
+    conditions = ['==', '<', '>', '>=', '<=', '!=']
+    def __init__(self, a, cond, b, lab_yes, lab_no):
+        assert cond in CJump.conditions 
+        self.a = a
+        self.cond = cond
+        self.b = b
+        self.Targets = [lab_yes, lab_no]
+
+    lab_yes = property(lambda s: s.Targets[0])
+    lab_no = property(lambda s: s.Targets[1])
+
+    def __repr__(self):
+        return 'IF {} {} {} THEN {} ELSE {}'.format(self.a, self.cond, self.b, self.lab_yes, self.lab_no)
+
+
+# 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', Temp).gen
+        self.newBlock2 = NamedClassGenerator('block', 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 = Function(name)
+        self.m.addFunc(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, Statement)
+        i.debugLoc = self.loc
+        if not self.bb:
+            raise Exception('No basic block')
+        self.bb.addInstruction(i)
+
+