from __future__ import absolute_import
from . import camel_case
from .types import ValueType
from .operands import Operand
from .formats import InstructionFormat
try:
from typing import Union, Sequence, List, Tuple, Any, TYPE_CHECKING from typing import Dict if TYPE_CHECKING:
from .ast import Expr, Apply, Var, Def, VarAtomMap from .typevar import TypeVar from .ti import TypeConstraint from .xform import XForm, Rtl
OpList = Union[Sequence[Operand], Operand]
ConstrList = Union[Sequence[TypeConstraint], TypeConstraint]
MaybeBoundInst = Union['Instruction', 'BoundInstruction']
InstructionSemantics = Sequence[XForm]
SemDefCase = Union[Rtl, Tuple[Rtl, Sequence[TypeConstraint]], XForm]
except ImportError:
pass
class InstructionGroup(object):
_current = None
def open(self):
assert InstructionGroup._current is None, (
"Can't open {} since {} is already open"
.format(self, InstructionGroup._current))
InstructionGroup._current = self
def close(self):
assert InstructionGroup._current is self, (
"Can't close {}, the open instuction group is {}"
.format(self, InstructionGroup._current))
InstructionGroup._current = None
def __init__(self, name, doc):
self.name = name
self.__doc__ = doc
self.instructions = [] self.open()
@staticmethod
def append(inst):
assert InstructionGroup._current, \
"Open an instruction group before defining instructions."
InstructionGroup._current.instructions.append(inst)
class Instruction(object):
ATTRIBS = {
'is_terminator': 'True for instructions that terminate the EBB.',
'is_branch': 'True for all branch or jump instructions.',
'is_indirect_branch':
'True for all indirect branch or jump instructions.',
'is_call': 'Is this a call instruction?',
'is_return': 'Is this a return instruction?',
'is_ghost': 'Is this a ghost instruction?',
'can_load': 'Can this instruction read from memory?',
'can_store': 'Can this instruction write to memory?',
'can_trap': 'Can this instruction cause a trap?',
'other_side_effects':
'Does this instruction have other side effects besides can_*',
'writes_cpu_flags': 'Does this instruction write to CPU flags?',
}
def __init__(self, name, doc, ins=(), outs=(), constraints=(), **kwargs):
self.name = name
self.camel_name = camel_case(name)
self.__doc__ = doc
self.ins = self._to_operand_tuple(ins)
self.outs = self._to_operand_tuple(outs)
self.constraints = self._to_constraint_tuple(constraints)
self.format = InstructionFormat.lookup(self.ins, self.outs)
self.semantics = None
self.number = None
self.value_results = tuple(
i for i, o in enumerate(self.outs) if o.is_value())
self.value_opnums = tuple(
i for i, o in enumerate(self.ins) if o.is_value())
self.imm_opnums = tuple(
i for i, o in enumerate(self.ins) if o.is_immediate())
self._verify_polymorphic()
for attr in kwargs:
if attr not in Instruction.ATTRIBS:
raise AssertionError(
"unknown instruction attribute '" + attr + "'")
for attr in Instruction.ATTRIBS:
setattr(self, attr, not not kwargs.get(attr, False))
if 'writes_cpu_flags' not in kwargs:
self.writes_cpu_flags = any(
out.is_cpu_flags() for out in self.outs)
InstructionGroup.append(self)
def __str__(self):
prefix = ', '.join(o.name for o in self.outs)
if prefix:
prefix = prefix + ' = '
suffix = ', '.join(o.name for o in self.ins)
return '{}{} {}'.format(prefix, self.name, suffix)
def snake_name(self):
if self.name == 'return':
return 'return_'
else:
return self.name
def blurb(self):
for line in self.__doc__.split('\n'):
line = line.strip()
if line:
return line
return ""
def _verify_polymorphic(self):
poly_ins = [
i for i in self.value_opnums
if self.ins[i].typevar.free_typevar()]
poly_outs = [
i for i, o in enumerate(self.outs)
if o.is_value() and o.typevar.free_typevar()]
self.is_polymorphic = len(poly_ins) > 0 or len(poly_outs) > 0
if not self.is_polymorphic:
return
self.use_typevar_operand = False
typevar_error = None
tv_op = self.format.typevar_operand
if tv_op is not None and tv_op < len(self.value_opnums):
try:
opnum = self.value_opnums[tv_op]
tv = self.ins[opnum].typevar
if tv is tv.free_typevar() or tv.singleton_type() is not None:
self.other_typevars = self._verify_ctrl_typevar(tv)
self.ctrl_typevar = tv
self.use_typevar_operand = True
except RuntimeError as e:
typevar_error = e
if not self.use_typevar_operand:
if len(self.outs) == 0:
if typevar_error:
raise typevar_error
else:
raise RuntimeError(
"typevar_operand must be a free type variable")
tv = self.outs[0].typevar
if tv is not tv.free_typevar():
raise RuntimeError("first result must be a free type variable")
self.other_typevars = self._verify_ctrl_typevar(tv)
self.ctrl_typevar = tv
def _verify_ctrl_typevar(self, ctrl_typevar):
other_tvs = [] for opnum in self.value_opnums:
typ = self.ins[opnum].typevar
tv = typ.free_typevar()
if tv is None or tv is ctrl_typevar:
continue
if typ is not tv:
raise RuntimeError(
"{}: type variable {} must be derived from {}"
.format(self.ins[opnum], typ.name, ctrl_typevar))
if tv in other_tvs:
raise RuntimeError(
"type variable {} can't be used more than once"
.format(tv.name))
other_tvs.append(tv)
for result in self.outs:
if not result.is_value():
continue
typ = result.typevar
tv = typ.free_typevar()
if tv is None or tv is ctrl_typevar:
continue
raise RuntimeError(
"type variable in output not derived from ctrl_typevar")
return other_tvs
def all_typevars(self):
if self.is_polymorphic:
return [self.ctrl_typevar] + self.other_typevars
else:
return []
@staticmethod
def _to_operand_tuple(x):
if isinstance(x, Operand):
y = (x,) else:
y = tuple(x)
for op in y:
assert isinstance(op, Operand)
return y
@staticmethod
def _to_constraint_tuple(x):
from .ti import TypeConstraint if isinstance(x, TypeConstraint):
y = (x,) else:
y = tuple(x)
for op in y:
assert isinstance(op, TypeConstraint)
return y
def bind(self, *args):
assert self.is_polymorphic
return BoundInstruction(self, args)
def __getattr__(self, name):
assert name != 'any', 'Wildcard not allowed for ctrl_typevar'
return self.bind(ValueType.by_name(name))
def fully_bound(self):
assert not self.is_polymorphic, self
return (self, ())
def __call__(self, *args):
from .ast import Apply return Apply(self, args)
def set_semantics(self, src, *dsts):
from semantics import verify_semantics
from .xform import XForm, Rtl
sem = [] for dst in dsts:
if isinstance(dst, Rtl):
sem.append(XForm(Rtl(src).copy({}), dst))
elif isinstance(dst, XForm):
sem.append(XForm(
dst.src.copy({}),
dst.dst.copy({}),
dst.constraints))
else:
assert isinstance(dst, tuple)
sem.append(XForm(Rtl(src).copy({}), dst[0],
constraints=dst[1]))
verify_semantics(self, Rtl(src), sem)
self.semantics = sem
class BoundInstruction(object):
def __init__(self, inst, typevars):
self.inst = inst
self.typevars = typevars
assert len(typevars) <= 1 + len(inst.other_typevars)
def __str__(self):
return '.'.join([self.inst.name, ] + list(map(str, self.typevars)))
def bind(self, *args):
return BoundInstruction(self.inst, self.typevars + args)
def __getattr__(self, name):
if name == 'any':
return self.bind(None)
return self.bind(ValueType.by_name(name))
def fully_bound(self):
if len(self.typevars) < 1 + len(self.inst.other_typevars):
unb = ', '.join(
str(tv) for tv in
self.inst.other_typevars[len(self.typevars) - 1:])
raise AssertionError("Unbound typevar {} in {}".format(unb, self))
assert len(self.typevars) == 1 + len(self.inst.other_typevars)
return (self.inst, self.typevars)
def __call__(self, *args):
from .ast import Apply return Apply(self, args)