Source code for microprobe.target.isa.operand

# 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