# Copyright 2011-2021 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""":mod:`microprobe.target.isa.operand` module
"""
# Futures
from __future__ import absolute_import, print_function
# Built-in modules
import abc
import os
import random
# Third party modules
# Own modules
from microprobe import MICROPROBE_RC
from microprobe.code.address import Address
from microprobe.code.var import Variable
from microprobe.exceptions import MicroprobeArchitectureDefinitionError, \
MicroprobeCodeGenerationError, MicroprobeValueError
from microprobe.target.isa.register import Register
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import OrderedDict, natural_sort
from microprobe.utils.typeguard_decorator import typeguard_testsuite
from microprobe.utils.yaml import read_yaml
# Constants
SCHEMA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "schemas",
"operand.yaml")
LOG = get_logger(__name__)
__all__ = [
"import_definition", "OperandDescriptor", "MemoryOperandDescriptor",
"MemoryOperand", "Operand", "OperandReg", "OperandImmRange",
"OperandValueSet", "OperandConst", "OperandConstReg",
"InstructionAddressRelativeOperand"
]
# Functions
[docs]
@typeguard_testsuite
def import_definition(filenames, inherits, registers):
"""
:param filenames:
:param registers:
"""
LOG.debug("Start")
operands = {}
operands_duplicated = {}
register_types = tuple([reg.type.name for reg in registers.values()])
for filename in filenames:
ope_data = read_yaml(filename, SCHEMA)
if ope_data is None:
continue
for elem in ope_data:
name = elem["Name"]
descr = elem.get("Description", "No description")
override = elem.get("Override", False)
key = []
try:
if "Registers" in elem:
regnames = elem["Registers"]
if isinstance(regnames, list):
if len(regnames) == 1 and \
regnames[0] in register_types:
regs = [
reg for reg in registers.values()
if reg.type.name == regnames[0]
]
else:
regs = [
registers[regname]
for regname in natural_sort(regnames)
]
key.append(tuple(regnames))
else:
regs = OrderedDict()
for regname in natural_sort(regnames):
regs[registers[regname]] = []
for regname2 in regnames[regname]:
regs[registers[regname]].append(
registers[regname2])
key.append(
tuple([(k, tuple(v))
for k, v in regnames.items()]))
address_base = elem.get("AddressBase", False)
address_index = elem.get("AddressIndex", False)
floating_point = elem.get("FloatingPoint", None)
vector = elem.get("Vector", None)
key.append(address_base)
key.append(address_index)
key.append(floating_point)
key.append(vector)
# Filter out Register without
# representation (N/A)
#
# These are pseudo registers used in
# simulation/emulation environment.
# They are not architected registers.
if isinstance(regs, list):
regs = [
reg for reg in regs if reg.representation != 'N/A'
]
elif isinstance(regs, dict):
for elem in regs:
regs[elem] = [
reg2 for reg2 in regs[elem]
if reg2.representation != 'N/A'
]
operand = OperandReg(name, descr, regs, address_base,
address_index, floating_point, vector)
elif "Min" in elem and "Max" in elem:
minval = elem["Min"]
maxval = elem["Max"]
step = elem.get("Step", 1)
novalues = elem.get("Except", [])
address_index = elem.get("AddressIndex", False)
shift = elem.get("Shift", 0)
add = elem.get("Add", 0)
key.append(minval)
key.append(maxval)
key.append(step)
key.append(tuple(novalues))
key.append(address_index)
key.append(shift)
key.append(add)
operand = OperandImmRange(name, descr, minval, maxval,
step, address_index, shift,
novalues, add)
elif "Values" in elem:
values = tuple(elem["Values"])
rep = elem.get("Representation", None)
key.append(tuple(values))
operand = OperandValueSet(name, descr, values, rep)
elif "Value" in elem:
value = elem["Value"]
key.append(value)
operand = OperandConst(name, descr, value)
elif "Register" in elem:
reg = registers[elem["Register"]]
address_base = elem.get("AddressBase", False)
address_index = elem.get("AddressIndex", False)
floating_point = elem.get("FloatingPoint", False)
vector = elem.get("Vector", False)
key.append(elem["Register"])
key.append(address_base)
key.append(address_index)
key.append(floating_point)
key.append(vector)
operand = OperandConstReg(name, descr, reg, address_base,
address_index, floating_point,
vector)
elif "Relative" in elem:
mindispl = elem["MinDisplacement"]
maxdispl = elem["MaxDisplacement"]
relative = elem["Relative"]
shift = elem.get("Shift", 0)
step = elem.get("Step", 1)
except_ranges = elem.get("ExceptRange", [])
key.append(mindispl)
key.append(maxdispl)
key.append(shift)
key.append(step)
key.append(tuple([tuple(elem) for elem in except_ranges]))
operand = InstructionAddressRelativeOperand(
name, descr, maxdispl, mindispl, shift, except_ranges,
relative, step)
else:
raise MicroprobeArchitectureDefinitionError(
"Operand definition '%s' in '%s' not supported" %
(name, filename))
tkey = tuple(key)
if tkey in operands_duplicated:
LOG.warning(
"Similar definition of operands: '%s' and"
" '%s'. Check if definition needed.", name,
operands_duplicated[tkey])
else:
operands_duplicated[tkey] = name
except KeyError as exception:
raise MicroprobeArchitectureDefinitionError(
"Definition"
" of operand '%s' "
"uses an unknown "
"register in '%s'"
"\nMissing defini"
"tion of: %s" % (name, filename, exception))
if name in operands and not override and filename not in inherits:
raise MicroprobeArchitectureDefinitionError(
"Duplicated definition of operand '%s' found in '%s'" %
(name, filename))
if name in operands:
LOG.debug("Redefined operand: %s", operand)
LOG.debug(operand)
operands[name] = operand
LOG.debug("End")
return operands
@typeguard_testsuite
def _format_integer(operand, value):
if MICROPROBE_RC['hex_all']:
if hex(value).endswith("L"):
return hex(value)[:-1]
return hex(value)
elif MICROPROBE_RC['hex_none']:
return str(value)
elif MICROPROBE_RC['hex_address']:
if (operand.address_relative or operand.address_immediate
or operand.address_absolute):
if hex(value).endswith("L"):
return hex(value)[:-1]
return hex(value)
else:
return str(value)
else:
raise NotImplementedError
# Classes
[docs]
@typeguard_testsuite
class OperandDescriptor:
"""Class to represent an operand descriptor.
"""
[docs]
def __init__(self, mtype, is_input, is_output):
"""
:param mtype:
:param is_input:
:param is_output:
"""
self._type = mtype
self._is_input = is_input
self._is_output = is_output
@property
def type(self):
"""Type of the operand descriptor (:class:`~.Operand`)"""
return self._type
@property
def is_input(self):
"""Is input flag (:class:`~.bool`) """
return self._is_input
@property
def is_output(self):
"""Is output flag (:class:`~.bool`) """
return self._is_output
[docs]
def set_type(self, new_type):
"""
:param new_type:
"""
self._type = new_type
[docs]
def copy(self):
"""Return a copy of the Operand descriptor.
:rtype: :class:`~.OperandDescriptor`
"""
return OperandDescriptor(self.type, self.is_input, self.is_output)
def __repr__(self):
return "%s(%s, %s, %s)" % (self.__class__.__name__, self._type,
self._is_input, self._is_output)
[docs]
@typeguard_testsuite
class MemoryOperandDescriptor:
"""Class to represent a memory operand descriptor.
"""
[docs]
def __init__(self, otype, io, bit_rate):
"""
:param otype:
:param io:
:param bit_rate:
"""
self._type = otype
self._is_load = "I" in io
self._is_store = "O" in io
self._is_prefetch = "P" in io
self._is_agen = "agen" in io
self._is_branch_target = "B" in io
self._bit_rate = bit_rate
assert (self.is_load or self.is_store) ^ self.is_agen \
^ self.is_prefetch ^ self.is_branch_target
@property
def type(self):
"""Memory operand descriptor type (:class:`~.Operand`)"""
return self._type
@property
def is_load(self):
"""Is load flag (:class:`~.bool`) """
return self._is_load
@property
def is_store(self):
"""Is store flag (:class:`~.bool`) """
return self._is_store
@property
def is_agen(self):
"""Is an address generator flag (:class:`~.bool`) """
return self._is_agen
@property
def is_prefetch(self):
"""Is prefech flag (:class:`~.bool`) """
return self._is_prefetch
@property
def is_branch_target(self):
"""Is a branch target (:class:`~.bool`) """
return self._is_branch_target
@property
def bit_rate(self):
"""Memory operand bit rate (::class:`~.int`) """
return self._bit_rate
def __str__(self):
"""Return string representation.
:rtype: :class:`~.str`
"""
rstr = "%s(%s," % (self.__class__.__name__, self.type)
if self.is_load:
rstr = "%sload," % rstr
if self.is_store:
rstr = "%sstore," % rstr
if self.is_prefetch:
rstr = "%sprefetch," % rstr
if self.is_agen:
rstr = "%saddress_generator," % rstr
if self.is_branch_target:
rstr = "%sbranch_target," % rstr
rstr = "%s)" % rstr
return rstr
[docs]
def full_report(self, tabs=0):
shift = ("\t" * (tabs + 1))
# rstr = shift + "Type : \n"
rstr = self.type.full_report(tabs=tabs) + "\n"
rstr += shift + "Load : %s\n" % self.is_load
rstr += shift + "Store : %s\n" % self.is_store
rstr += shift + "Prefetch : %s\n" % self.is_prefetch
rstr += shift + "Address generator : %s\n" % self.is_agen
rstr += shift + "Branch target : %s" % self.is_branch_target
return rstr
[docs]
@typeguard_testsuite
class MemoryOperand:
"""This represents a machine instruction memory operand. It contains
the operands, the formula, the
"""
_cmp_attributes = ["_address", "_length"]
[docs]
def __init__(self, address_formula, length_formula):
"""
:param address_formula:
:param length_formula:
"""
self._address = address_formula
self._length = length_formula
@property
def address_operands(self):
""" """
return self._address
@property
def length_operands(self):
return self._length
def _check_cmp(self, other):
if not isinstance(other, self.__class__):
raise NotImplementedError("%s != %s" %
(other.__class__, self.__class__))
def __eq__(self, other):
"""x.__eq__(y) <==> x==y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if not getattr(self, attr) == getattr(other, attr):
return False
return True
def __ne__(self, other):
"""x.__ne__(y) <==> x!=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if not getattr(self, attr) == getattr(other, attr):
return True
return False
def __lt__(self, other):
"""x.__lt__(y) <==> x<y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) < getattr(other, attr):
return True
elif getattr(self, attr) > getattr(other, attr):
return False
return False
def __gt__(self, other):
"""x.__gt__(y) <==> x>y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) > getattr(other, attr):
return True
elif getattr(self, attr) < getattr(other, attr):
return False
return False
def __le__(self, other):
"""x.__le__(y) <==> x<=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) <= getattr(other, attr):
continue
else:
return False
return True
def __ge__(self, other):
"""x.__ge__(y) <==> x>=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) >= getattr(other, attr):
continue
else:
return False
return True
def __str__(self):
return "%s(Address: %s, Length: %s)" % (self.__class__.__name__,
self._address, self._length)
[docs]
def full_report(self, tabs=0):
shift = ("\t" * (tabs + 1))
rstr = shift + \
"Address : %s\n" % list(self.address_operands.keys())
rstr += shift + "Length : %s" % list(self.length_operands.keys())
return rstr
[docs]
@typeguard_testsuite
class Operand(abc.ABC):
"""This represents a machine instruction operand"""
_cmp_attributes = [
"_name", "_descr", "_ai", "_ab", "_aim", "_imm", "_const", "_rel",
"_rela", "_fp", "_vector"
]
[docs]
@abc.abstractmethod
def __init__(self, name, descr):
"""
:param name:
:param descr:
"""
self._name = name
self._descr = descr
self._ai = False
self._ab = False
self._aim = False
self._imm = False
self._const = False
self._rel = False
self._rela = False
self._fp = False
self._vector = False
@property
def name(self):
"""Operand name (:class:`~.str`)."""
return self._name
@property
def description(self):
"""Operand description (:class:`~.str`)."""
return self._descr
@property
def address_relative(self):
"""Operand is for generating relative addresses (:class:`~.bool`)."""
return self._rel
@property
def address_absolute(self):
"""Operand is for generating absolute addresses (:class:`~.bool`)."""
return self._rela
@property
def address_immediate(self):
"""Operand is an immediate of an address (:class:`~.bool`)."""
return self._aim
@property
def float(self):
"""Operand is float (:class:`~.bool`)."""
return self._fp
@property
def address_base(self):
"""Operand is the base register for an address (:class:`~.bool`)."""
return self._ab
@property
def address_index(self):
"""Operand is the index register for an address (:class:`~.bool`)."""
return self._ai
@property
def immediate(self):
"""Operand is immediate (:class:`~.bool`)."""
return self._imm
@property
def vector(self):
"""Operand is vector (:class:`~.bool`)."""
return self._vector
@property
def constant(self):
"""Operand is constant (:class:`~.bool`)."""
return self._const
[docs]
@abc.abstractmethod
def copy(self):
"""Return a copy of the operand. """
raise NotImplementedError
[docs]
@abc.abstractmethod
def values(self):
"""Return the possible value of the operand."""
raise NotImplementedError
[docs]
@abc.abstractmethod
def random_value(self):
"""Return a random possible value for the operand."""
raise NotImplementedError
[docs]
@abc.abstractmethod
def representation(self, value):
"""Return the string representation of the operand.
:param value: value of the operand
:type value: :class:`~.str`, :class:`~.Register` or
:class:`int`
:rtype: :class:`~.str`
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def codification(self, value):
"""Return the binary codification of the operand.
:param value: value of the operand.
:type value: :class:`~.str`, :class:`~.Register` or
:class:`int`
:rtype: :class:`~.str`
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def access(self, value):
"""
:param value:
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def set_valid_values(self, values):
"""Sets the set of valid value for the operand.
:param value: value of the operand.
:type value: :class:`list` of :class:`~.str`,
:class:`~.Register` or :class:`int`
"""
raise NotImplementedError
@abc.abstractmethod
def __contains__(self, value):
"""
:param value:
"""
raise NotImplementedError
[docs]
def check(self, value):
"""Check if a value is valid for the operand.
:param value: value of the operand.
:type value: :class:`~.str`, :class:`~.Register` or
:class:`int`
:raise microprobe.exceptions.MicroprobeValueError: if
the value is not allowed for the operand
"""
if not self.__contains__(value):
raise MicroprobeValueError("Invalid operand value %s not in %s" %
(value, list(self.values())))
def __str__(self):
return "%-8s : %s (%s)" % (self.name, self.description,
self.__class__.__name__)
def __repr__(self):
return "%s(\"%s\", \"%s\")" % (self.__class__.__name__, self.name,
self.description)
def _check_cmp(self, other):
if not isinstance(other, self.__class__):
raise NotImplementedError("%s != %s" %
(other.__class__, self.__class__))
def __eq__(self, other):
"""x.__eq__(y) <==> x==y"""
if not isinstance(other, self.__class__):
return False
for attr in self._cmp_attributes:
if not getattr(self, attr) == getattr(other, attr):
return False
return True
def __ne__(self, other):
"""x.__ne__(y) <==> x!=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if not getattr(self, attr) == getattr(other, attr):
return True
return False
def __lt__(self, other):
"""x.__lt__(y) <==> x<y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) < getattr(other, attr):
return True
elif getattr(self, attr) > getattr(other, attr):
return False
return False
def __gt__(self, other):
"""x.__gt__(y) <==> x>y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) > getattr(other, attr):
return True
elif getattr(self, attr) < getattr(other, attr):
return False
return False
def __le__(self, other):
"""x.__le__(y) <==> x<=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) <= getattr(other, attr):
continue
else:
return False
return True
def __ge__(self, other):
"""x.__ge__(y) <==> x>=y"""
self._check_cmp(other)
for attr in self._cmp_attributes:
if getattr(self, attr) >= getattr(other, attr):
continue
else:
return False
return True
[docs]
@typeguard_testsuite
class OperandReg(Operand):
"""Class to represent a register operand.
"""
[docs]
def __init__(self, name, descr, regs, address_base, address_index,
floating_point, vector):
"""
:param name:
:param descr:
:param regs:
:param address_base:
:param address_index:
:param floating_point:
:param vector:
"""
super(OperandReg, self).__init__(name, descr)
if isinstance(regs, list):
self._regs = OrderedDict()
for reg in regs:
self._regs[reg] = [reg]
else:
self._regs = regs
self._ab = address_base
self._ai = address_index
self._fp = floating_point
self._vector = vector
if self._fp is None:
self._fp = list(set([reg.type for reg in self._regs
]))[0].used_for_float_arithmetic
if self._vector is None:
self._vector = list(set([reg.type for reg in self._regs
]))[0].used_for_vector_arithmetic
[docs]
def values(self):
"""Return the possible value of the operand.
:rtype: :class:`list` of :class:`~.Register`
"""
return list(self._regs.keys())
[docs]
def representation(self, value):
"""
:param value:
"""
return value.representation
[docs]
def codification(self, value):
"""
:param value:
"""
return value.codification
[docs]
def random_value(self):
"""Return a random possible value for the operand.
:rtype: :class:`~.Register`
"""
return list(self._regs.keys())[random.randrange(0, len(self._regs))]
[docs]
def access(self, value):
"""
:param value:
"""
return self._regs[value]
def __contains__(self, value):
"""
:param value:
"""
if not isinstance(value, Register):
return False
return value.name in [reg.name for reg in self.values()]
[docs]
def copy(self):
return OperandReg(self.name, self.description, self._regs.copy(),
self._ab, self._ai, self._fp, self._vector)
[docs]
def set_valid_values(self, values):
"""
:param values:
"""
assert len(values) > 0
for value in self.values():
if value not in values:
del self._regs[value]
assert sorted(self.values()) == sorted(values), \
"\nValues: %s \nValues(): %s" % (sorted(values),
sorted(self.values()))
self._const = len(values) == 1
[docs]
@typeguard_testsuite
class OperandImmRange(Operand):
"""Class to represent a immediate range operand.
"""
[docs]
def __init__(self, name, descr, minvalue, maxvalue, step, aim, shift,
novalues, add):
"""
:param name:
:param descr:
:param minvalue:
:param maxvalue:
:param step:
:param aim:
:param shift:
:param novalues:
:param add:
"""
super(OperandImmRange, self).__init__(name, descr)
self._min = minvalue
self._max = maxvalue
self._step = step
self._aim = aim # Address Immediate?
self._shift = shift
self._imm = True
self._novalues = novalues
self._add = add
self._computed_values = None
[docs]
def copy(self):
return OperandImmRange(self.name, self.description, self._min,
self._max, self._step, self._aim, self._shift,
self._novalues, self._add)
[docs]
def values(self):
"""Return the possible value of the operand.
:rtype: list of ::class:`~.int`
"""
if self._computed_values is None:
self._computed_values = [
elem for elem in range(self._min, self._max + 1, self._step)
if elem not in self._novalues
]
return self._computed_values
[docs]
def set_valid_values(self, values):
"""
:param values:
"""
if len(values) == 0:
raise MicroprobeCodeGenerationError(
"Setting an operand without any valid value. Please check "
"the definition files. Previous value: '%s'. New values: '%s'"
"." % (list(self.values()), values))
for value in values:
assert value in list(self.values())
self._computed_values = values
self._const = len(values) == 1
[docs]
def random_value(self):
"""Return a random possible value for the operand.
:rtype: ::class:`~.int`
"""
if self._computed_values is not None:
return self._computed_values[random.randrange(
0, len(self._computed_values))]
value = random.randrange(self._min, self._max + 1, self._step)
if value not in self._novalues:
return value
else:
return self.random_value()
[docs]
def representation(self, value):
"""
:param value:
"""
# Immediate displacements sometimes contain
# variable names instead of a number
if isinstance(value, str):
# print value
return value
# return _format_integer(self, (value >> self._shift) + self._add)
return _format_integer(self, value + self._add)
[docs]
def codification(self, value):
"""
:param value:
"""
return str(value >> self._shift)
@property
def max(self):
return self._max
@property
def min(self):
return self._min
@property
def step(self):
return self._step
@property
def shift(self):
return self._shift
@property
def add(self):
return self._add
[docs]
def check(self, value):
"""
:param value:
"""
if not isinstance(value, int):
raise MicroprobeValueError("Invalid operand value: '%s'. Integer"
" required and '%s' provided" %
(value, type(value)))
# value = value >> self._shift
if value <= self._max and value >= self._min \
and (value - self._min) % self._step == 0 \
and value not in self._novalues:
return True
else:
raise MicroprobeValueError("Invalid operand value: %d (max: %d,"
" min: %d)" %
(value, self._max, self._min))
[docs]
def access(self, dummy):
"""
:param dummy:
"""
return []
def __contains__(self, value):
"""
:param value:
"""
raise NotImplementedError
[docs]
@typeguard_testsuite
class OperandValueSet(Operand):
"""Class to represent a value set operand.
"""
[docs]
def __init__(self, name, descr, values, rep):
"""
:param name:
:param descr:
:param values:
"""
super(OperandValueSet, self).__init__(name, descr)
# TODO: add input value checking
self._values = values
self._imm = True
self._rep = None
if rep is not None and len(rep) != len(values):
raise MicroprobeArchitectureDefinitionError(
"Values and representation of operand definition "
"'%s' do not have the same length." % name)
if rep is not None:
self._rep = dict(zip(values, rep))
[docs]
def copy(self):
return OperandValueSet(self.name, self.description, self._values,
self._rep)
[docs]
def values(self):
"""Return the possible value of the operand.
:rtype: list of ::class:`~.int`
"""
return self._values
[docs]
def representation(self, value):
"""
:param value:
"""
if self._rep is None:
return _format_integer(self, value)
return self._rep[value]
[docs]
def codification(self, value):
"""
:param value:
"""
return str(value)
[docs]
def random_value(self):
"""Return a random possible value for the operand.
:rtype: ::class:`~.int`
"""
return self._values[random.randrange(0, len(self._values))]
[docs]
def access(self, dummy):
"""
:param dummy:
"""
return []
@property
def shift(self):
return 0
@property
def min(self):
return min(self._values)
def __contains__(self, value):
"""
:param value:
"""
return value in list(self.values())
[docs]
def set_valid_values(self, values):
"""
:param values:
"""
assert len(values) > 0
for value in values:
assert value in list(self.values())
self._values = values
self._const = len(values) == 1
[docs]
@typeguard_testsuite
class OperandConst(Operand):
"""Class to represent a constant operand.
"""
[docs]
def __init__(self, name, descr, value, aim=False, arel=False):
"""
:param name:
:param descr:
:param value:
"""
super(OperandConst, self).__init__(name, descr)
self._value = value
self._imm = True
self._aim = aim
self._rel = arel
self._const = True
[docs]
def copy(self):
return OperandConst(self.name, self.description, self._value)
[docs]
def values(self):
"""Return the possible value of the operand.
:rtype: list of ::class:`~.int`
"""
return [self._value]
[docs]
def representation(self, value):
"""
:param value:
"""
return _format_integer(self, value)
[docs]
def codification(self, value):
"""
:param value:
"""
return str(value)
[docs]
def random_value(self):
"""Return a random possible value for the operand.
:rtype: ::class:`~.int`
"""
return self._value
@property
def shift(self):
return 0
@property
def min(self):
return self._value
[docs]
def access(self, dummy):
"""
:param dummy:
"""
return []
def __contains__(self, value):
"""
:param value:
"""
return value in list(self.values())
[docs]
def set_valid_values(self, values):
"""
:param values:
"""
assert len(values) == 1
for value in values:
assert value in list(self.values())
self._value = values[0]
[docs]
@typeguard_testsuite
class OperandConstReg(Operand):
"""Class to represent a constant register operand.
"""
[docs]
def __init__(self, name, descr, reg, address_base, address_index,
floating_point, vector):
"""
:param name:
:param descr:
:param reg:
:param address_base:
:param address_index:
:param floating_point:
:param vector:
"""
super(OperandConstReg, self).__init__(name, descr)
self._reg = reg
self._regs = [reg]
self._const = True
self._ab = address_base
self._ai = address_index
self._fp = floating_point
self._vector = vector
if self._fp is None:
self._fp = list(set([reg.type for reg in self._regs
]))[0].used_for_float_arithmetic
if self._vector is None:
self._vector = list(set([reg.type for reg in self._regs
]))[0].used_for_float_arithmetic
[docs]
def copy(self):
return OperandConstReg(self.name, self.description, self._reg,
self._ab, self._ai, self._fp, self._vector)
[docs]
def values(self):
"""Return the possible value of the operand.
:rtype: list of :class:`~.Register`
"""
return [self._reg]
[docs]
def random_value(self):
"""Return a random possible value for the operand.
:rtype: :class:`~.Register`
"""
return self._reg
[docs]
def representation(self, value):
"""
:param value:
"""
return value.representation
[docs]
def codification(self, value):
"""
:param value:
"""
return value.codification
[docs]
def access(self, value):
"""
:param value:
"""
return [value]
def __contains__(self, value):
"""
:param value:
"""
if not isinstance(value, Register):
return False
return value.name in [reg.name for reg in self.values()]
[docs]
def set_valid_values(self, values):
"""
:param values:
"""
assert len(values) == 1
for value in values:
assert value in list(self.values())
self._reg = values[0]
[docs]
@typeguard_testsuite
class InstructionAddressRelativeOperand(Operand):
"""Class to represent a relative instruction address operand.
Relative instruction address operands are used for immediates operands
used to compute relative distance between the current instruction
and the target. Examples are : branch relative, or load address relative.
"""
[docs]
def __init__(self, name, descr, maxdispl, mindispl, shift, except_range,
relative, step):
"""Create a InstructionAddressRelativeOperand object.
:param name: Operand name
:type name: :class:`~.str`
:param descr: Operand description
:type descr: :class:`~.str`
:param maxdispl: Maximum displacement allowed
:type maxdispl: ::class:`~.int`
:param mindispl: Minimum displacement allowed
:type mindispl: ::class:`~.int`
:param shift: Number of shifted bits
:type shift: ::class:`~.int`
:param except_range: list of forbidden ranges for displacement. Ranges
are represented using (lower_bound, upper_bound)
:type except_range: :class:`~.list` of \
:func:`tuple` with :class:`~.int`
:rtype: :class:`~.InstructionAddressRelativeOperand`
"""
super(InstructionAddressRelativeOperand, self).__init__(name, descr)
self._maxdispl = maxdispl
self._mindispl = mindispl
self._rel = relative
self._rela = not relative
self._shift = shift
self._except = except_range
self._step = step
[docs]
def copy(self):
return InstructionAddressRelativeOperand(self.name, self.description,
self._maxdispl,
self._mindispl, self._shift,
self._except, self._rel,
self._step)
[docs]
def values(self):
"""Return the possible value of the operand.
:rtype: list of ::class:`~.int`
"""
return [self._mindispl << self._shift]
[docs]
def random_value(self):
"""Return a random possible value for the operand.
:rtype: ::class:`~.int`
"""
value = random.randrange(self._mindispl, self._maxdispl) << self._shift
if value <= (self._maxdispl << self._shift) and \
value >= (self._mindispl << self._shift) and \
not self._in_except_ranges(value) and \
value % self._step == 0:
return value
else:
return self.random_value()
return value
[docs]
def representation(self, value):
"""
:param value:
"""
assert isinstance(value, tuple([int, Address]))
if isinstance(value, int):
# print(value, self._shift)
# assert value % (self._shift + 1) == 0
return _format_integer(self, value)
else:
base_address = value.base_address
displacement = value.displacement
if isinstance(base_address, Variable):
str_value = base_address.name
elif isinstance(base_address, str):
str_value = base_address
else:
raise MicroprobeCodeGenerationError(
"Unable to generate the string representation of '%s'"
" with value: '%s'" % (self, value))
if displacement > 0:
str_value = "%s+0x%x" % (str_value, displacement)
elif displacement < 0:
str_value = "%s-0x%x" % (str_value, abs(displacement))
return str_value
def _in_except_ranges(self, value):
"""
:param value:
"""
for irange in self._except:
if value >= (irange[0] << self._shift) \
and value <= (irange[1] << self._shift):
return True
return False
[docs]
def check(self, value):
"""
:param value:
"""
if isinstance(value, int):
cvalue = value
elif isinstance(value, Address):
# Warning!
return
else:
if not isinstance(value[0], Address) or \
not isinstance(value[1], Address):
raise MicroprobeValueError("Invalid operand value '%s'."
" Any Address?" % (value))
cvalue = value[0] - value[1]
if cvalue > (self._maxdispl << self._shift) or \
cvalue < (self._mindispl << self._shift) or \
self._in_except_ranges(cvalue) or \
cvalue % self._step != 0:
raise MicroprobeValueError(
"Invalid operand value '%d' "
"not within the"
" allowed range (%d, %d) and exceptions"
" '%s' " %
(cvalue, self._mindispl, self._maxdispl, self._except))
[docs]
def codification(self, value):
"""
:param value:
"""
if isinstance(value, int):
return str(value >> self._shift)
elif isinstance(value, Address):
raise MicroprobeCodeGenerationError("Unable to codify the"
" symbolic address: %s ."
" Consider to add a pass to"
" translate them to actual "
"values " % value)
else:
raise NotImplementedError
@property
def shift(self):
return self._shift
[docs]
def access(self, dummy):
"""
:param dummy:
"""
return []
[docs]
def set_valid_values(self, values):
"""
:param values:
"""
raise NotImplementedError
def __contains__(self, value):
"""
:param value:
"""
raise NotImplementedError