view python/ir/instruction.py @ 262:ed14e077124c

Added conditional branch instructions
author Windel Bouwman
date Fri, 09 Aug 2013 11:30:11 +0200
parents 444b9df2ed99
children f8b5da5784b8
line wrap: on
line source

from .basicblock import BasicBlock
from .function import Function


class Value:
    """ Temporary SSA value (value that is assigned only once! """
    def __init__(self, name):
        # TODO: add typing? for now only handle integers
        self.name = name
        self.used_by = []
        self.Setter = None

    def __repr__(self):
        return '{0}'.format(self.name) # + str(self.IsUsed)

    @property
    def UsedInBlocks(self):
        bbs = [i.parent for i in self.used_by + [self.Setter]]
        assert all(isinstance(bb, BasicBlock) for bb in bbs)
        return set(bbs)

    @property
    def IsUsed(self):
        return len(self.used_by) > 0

    Used = IsUsed

    def onlyUsedInBlock(self, bb):
        return all(use.Block is bb for use in self.used_by)

    def lastUse(self, ins):
        assert ins in self.used_by
        return all(not ins.precedes(ub) for ub in self.used_by)

    def replaceby(self, v2):
        for use_ins in self.used_by:
            use_ins.replaceValue(self, v2.value)
        assert not self.Used


class Variable(Value):
    pass


class Use:
    def __init__(self, user, val):
        self.user = user
        assert isinstance(val, Value)
        self.val = val
        self.val.used_by.append(self.user)

    def delete(self):
        self.val.used_by.remove(self.user)


class Instruction:
    """ Base class for all instructions. """
    def __init__(self):
        # live variables at this node:
        self.live_in = set()
        self.live_out = set()
        # What variables this instruction uses and defines:
        self.defs = []
        self.uses = []

    def delete(self):
        while self.uses:
            use = self.uses.pop()
            use.delete()
        while self.defs:
            d = self.defs.pop()
            d.Setter = None

    def addUse(self, val):
        self.uses.append(Use(self, val))

    def removeUse(self, val):
        for u in self.uses:
            if u.val is val:
                theUse = u
        theUse.delete()
        self.uses.remove(theUse)

    def addDef(self, v):
        self.defs.append(v)
        assert v.Setter == None
        v.Setter = self

    def removeDef(self, v):
        assert v.Setter is self
        v.Setter = None
        self.defs.remove(v)

    def getParent(self):
        return self.parent

    def setParent(self, p):
        self.parent = p
    Parent = property(getParent, setParent)

    def replaceValue(self, old, new):
        # TODO: make this a generic function
        raise NotImplementedError('{}'.format(type(self)))

    @property
    def Position(self):
        return self.Block.Instructions.index(self)

    def precedes(self, other):
        assert isinstance(other, Instruction)
        if self.Block is other.Block:
            return other.Position > self.Position
        else:
            return self.Block.precedes(other.Block)

    def follows(self, other):
        pass

    @property
    def Function(self):
        return self.Block.parent

    @property
    def Block(self):
        return self.Parent

    def check(self):
        # Check that the variables defined by this instruction 
        # are only used in the same block
        for v in self.defs:
            assert v.Setter is self
            for ub in v.used_by:
                assert ub.Function == self.Function

        # Check that variables used are defined earlier:
        for u in self.uses:
            v = u.val
            #assert self.Position > v.Setter.Position






# Function calling:
class Call(Instruction):
   def __init__(self, callee, arguments, result=None):
      super().__init__()
      self.callee = callee
      assert type(callee) is Function
      self.arguments = arguments
      for arg in arguments:
         assert type(arg) is Value
         self.addUse(arg)
      self.result = result
      if result:
         assert type(result) is Value
         self.addDef(result)

   def __repr__(self):
      if self.result:
         pfx = '{0} = '.format(self.result)
      else:
         pfx = ''
      args = ','.join([str(arg) for arg in self.arguments])
      return pfx + '{0}({1})'.format(self.callee.name, args)


class Alloc(Instruction):
    """ Allocates space on the stack """
    def __init__(self, value):
        super().__init__()
        assert isinstance(value, Value)
        self.value = value
        self.addDef(value)

    def __repr__(self):
        return '{0} = alloc'.format(self.value)


class ImmLoad(Instruction):
    def __init__(self, target, value):
        super().__init__()
        assert type(target) is Value
        self.target = target
        self.value = value
        self.addDef(target)

    def __repr__(self):
        return '{} = {}'.format(self.target, self.value)


# Data operations
class BinaryOperator(Instruction):
    def __init__(self, result, operation, value1, value2):
        super().__init__()
        assert type(value1) is Value, str(value1) + str(type(value1))
        assert type(value2) is Value, value2
        self.result = result
        self.addDef(result)
        self.value1 = value1
        self.value2 = value2
        self.addUse(value1)
        self.addUse(value2)
        self.operation = operation

    def __repr__(self):
        a, b = self.value1, self.value2
        return '{} = {} {} {}'.format(self.result, a, self.operation, b)

    def replaceValue(self, old, new):
        if old is self.value1:
            self.value1 = new
        elif old is self.value2:
            self.value2 = new
        elif old is self.result:
            self.result = new
        else:
            raise Exception()
        self.removeUse(old)
        self.addUse(new)


# Memory functions:
class Load(Instruction):
    def __init__(self, location, value):
        super().__init__()
        assert type(value) is Value
        assert isinstance(location, Value), "Location must be a value"
        self.value = value
        self.addDef(value)
        self.location = location
        self.addUse(self.location)

    def __repr__(self):
        return '{} = [{}]'.format(self.value, self.location)


class Store(Instruction):
    def __init__(self, location, value):
        super().__init__()
        assert type(value) is Value, value
        assert isinstance(location, Value), "Location must be a value"
        self.location = location
        self.value = value
        self.addUse(value)
        self.addUse(location)

    def __repr__(self):
        return '[{}] = {}'.format(self.location, self.value)

    def replaceValue(self, old, new):
        if old is self.value:
            self.value = new
        elif old is self.location:
            self.location = new
        else:
            raise Exception()
        self.removeUse(old)
        self.addUse(new)


# Branching:
class Terminator(Instruction):
    @property
    def Targets(self):
        return self.getTargets()

    def changeTarget(self, tfrom, tto):
        pass


class Branch(Terminator):
    def __init__(self, target):
        super().__init__()
        assert type(target) is BasicBlock
        self.target = target

    def __repr__(self):
        return 'BRANCH {0}'.format(self.target)

    def getTargets(self):
        return [self.target]

    def changeTarget(self, tfrom, tto):
        assert tfrom is self.target
        self.target = tto


class ConditionalBranch(Terminator):
   def __init__(self, a, cond, b, lab1, lab2):
      super().__init__()
      self.a = a
      assert type(a) is Value
      self.cond = cond
      assert cond in ['==', '<', '>']
      self.b = b
      self.addUse(a)
      self.addUse(b)
      assert type(b) is Value
      assert type(lab1) is BasicBlock
      self.lab1 = lab1
      assert type(lab2) is BasicBlock
      self.lab2 = lab2

   def __repr__(self):
      return 'IF {0} {1} {2} THEN {3} ELSE {4}'.format(self.a, self.cond, self.b, self.lab1, self.lab2)
   def getTargets(self):
      return [self.lab1, self.lab2]

   def changeTarget(self, tfrom, tto):
      assert tfrom is self.lab1 or tfrom is self.lab2
      if tfrom is self.lab1:
         self.lab1 = tto
      elif tfrom is self.lab2:
         self.lab2 = tto


class Return(Terminator):
    def __init__(self, value=None):
        super().__init__()
        self.value = value
        if value:
            self.addUse(value)

    def __repr__(self):
        if self.value:
            return 'ret {0}'.format(self.value)
        else:
            return 'ret'

    def getTargets(self):
        return []


class PhiNode(Instruction):
    def __init__(self):
        super().__init__()
        self.incBB = []

    def addIncoming(self, bb):
        self.incBB.append(bb)