view python/ir/instruction.py @ 239:63bb40758066

added check
author Windel Bouwman
date Mon, 22 Jul 2013 17:57:25 +0200
parents 88a1e0baef65
children c4370696ccc7
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 IsUsed(self):
        return len(self.used_by) > 0

    def onlyUsedInBlock(self, bb):
        for use in self.used_by:
            ins = use
            if ins.parent != bb:
                return False
        return True


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):
        raise NotImplementedError()

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

    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.parent == self.parent

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




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

    def changeTarget(self, tfrom, tto):
        pass


# 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 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 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__()
      #print('operation is in binops:', operation in BinOps)
      # Check types of the two operands:
      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)

# Branching:
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 PhiNode(Instruction):
   def __init__(self):
      super().__init__()
      self.incBB = []
   def addIncoming(self, bb):
      self.incBB.append(bb)