Source code for microprobe.code.context

# 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.code.context` module

"""

# Futures
from __future__ import absolute_import, print_function, annotations

# Built-in modules
from typing import TYPE_CHECKING, Tuple, Dict, List

# Third party modules
import six

# Own modules
from microprobe.code.address import Address, InstructionAddress
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import RejectingDict, smart_copy_dict

# Type hints
if TYPE_CHECKING:
    from microprobe.target.isa.register import Register
    from microprobe.code.address import MemoryValue
    from microprobe.target.isa.dat import DynamicAddressTranslation

# Constants
LOG = get_logger(__name__)
__all__ = ["Context"]

# Functions


# Classes
[docs]class Context(object): # pylint: disable=too-many-public-methods """Class to represent the execution context (e.g. register values, etc ... on each benchmark building block) """
[docs] def __init__( self, default_context: Context | None = None, code_segment: int | None = None, data_segment: int | None = None, symbolic: bool = True, absolute: bool = False ): """ :param default_context: (Default value = None) :param code_segment: (Default value = None) :param data_segment: (Default value = None) :param symbolic: (Default value = True) """ self._reserved_registers: Dict[str, Register] = RejectingDict() self._register_values: Dict[ Register, int | float | Address | str | None ] = {} self._value_registers: Dict[ int | float | Address | str, List[Register] ] = {} self._memory_values: Dict[Address, MemoryValue] = {} self._value_memorys: Dict[MemoryValue, List[MemoryValue]] = {} self._data_segment = data_segment self._code_segment = code_segment self._symbolic = symbolic self._fabsolute = absolute self._dat = None if default_context is not None: self = default_context.copy()
[docs] def copy(self): """Returns a copy of the current context.""" newcontext = Context() # pylint: disable=protected-access newcontext._reserved_registers = smart_copy_dict( self._reserved_registers ) newcontext._register_values = smart_copy_dict(self._register_values) newcontext._value_registers = smart_copy_dict(self._value_registers) newcontext._memory_values = smart_copy_dict(self._memory_values) newcontext._value_memorys = smart_copy_dict(self._value_memorys) if self._dat is not None: newcontext.set_dat(self._dat.copy()) newcontext.set_code_segment(self.code_segment) newcontext.set_data_segment(self.data_segment) newcontext.set_symbolic(self.symbolic) newcontext.set_absolute(self.force_absolute) return newcontext
[docs] def add_reserved_registers(self, rregs: List[Register]): """Add the provided registers into the reserved register list. :param rregs: Registers to reserve :type rregs: :class:`~.list` of :class:`~.Register` """ for reg in rregs: self._reserved_registers[reg.name] = reg
[docs] def remove_reserved_registers(self, rregs: List[Register]): """Remove the provided registers from the reserved register list. :param rregs: Registers to un-reserve :type rregs: :class:`~.list` of :class:`~.Register` """ for reg in rregs: del self._reserved_registers[reg.name]
[docs] def set_register_value( self, register: Register, value: int | float | Address | str ): """Set the provided register to the specified value. :param register: Register to set :type register: :class:`~.Register` :param value: Value to assign :type value: :class:`~.int`, :class:`~.float`, :class:`~.long`, :class:`~.Address` or :class:`~.str` """ LOG.debug("Setting %s to %s", register.name, value) assert isinstance( value, tuple(list(six.integer_types) + [float, Address, InstructionAddress, str]) ), type(value) if isinstance(value, str): assert len(value.split("_")) == 2 if self.get_register_value(register) is not None: self.unset_register(register) self._register_values[register] = value if value in self._value_registers: if register not in self._value_registers[value]: self._value_registers[value].append(register) else: self._value_registers[value] = [register]
# assert self.get_register_value(register) == value # assert value in self._value_registers.keys() # assert self._register_values[register] == value
[docs] def get_closest_value(self, value: int | float | Address | str): """Returns the closest value to the given value. Returns the closest value to the given value. If there are not values registered, `None` is returned. :param value: value to look for :type value: :class:`~.int`, :class:`~.float`, :class:`~.long`, :class:`~.Address` or :class:`~.str` """ possible_regs: List[Tuple[Register, int | float | Address | str]] = [] for reg, val in self._register_values.items(): if not isinstance(val, type(value)): continue possible_regs.append((reg, val)) possible_regs = sorted(possible_regs, key=lambda x: abs(x[1] - value)) if possible_regs: return possible_regs[0] return None
[docs] def get_closest_address_value(self, address: Address): """Returns the closest address to the given address. Returns the closest address to the given address. If there are not addresses registered, `None` is returned. :param address: Address to look for :type address: :class:`~.Address` """ possible_regs: List[Tuple[Register, Address]] = [] for reg, value in self._register_values.items(): if not isinstance(value, Address): continue if Address(base_address=value.base_address) == \ Address(base_address=address.base_address): possible_regs.append((reg, value)) possible_regs = sorted(possible_regs, key=lambda x: abs(x[1].displacement - address.displacement)) if possible_regs: return possible_regs[0] return None
[docs] def get_register_closest_value(self, value: int | float | str): """Returns the register with the closest value to the given value. Returns the register with the closest value to the given value. If there are not values registered, `None` is returned. Address values are ignored. :param value: Value to look for :type value: :class:`~.int`, :class:`~.float`, :class:`~.long`, :class:`~.Address` or :class:`~.str` """ possible_regs: List[Tuple[Register, int | float | str]] = [] for reg, reg_value in self._register_values.items(): if isinstance(value, Address): continue if not isinstance(reg_value, type(value)): continue if isinstance(reg_value, str): if reg_value.split("_")[1] != value.split(" ")[1]: continue possible_regs.append((reg, reg_value)) if not isinstance(value, str): possible_regs = sorted(possible_regs, key=lambda x: abs(x - value)) else: possible_regs = sorted(possible_regs, key=lambda x: abs(int(x.split("_")[0]) - int(value.split("_")[0]))) if possible_regs: return possible_regs[0] return None
[docs] def get_register_value(self, register: Register): """Returns the register value. `None` if not found. :param register: Register to get its value :type register: :class:`~.Register` """ value = self._register_values.get(register, None) if value is not None: assert value in self._value_registers.keys() return value
[docs] def get_registername_value(self, register_name: str): """Returns the register value. `None` if not found. :param register: Register name to get its value :type register: :class:`~.str` :param register_name: """ assert isinstance(register_name, str) register_names = [reg.name for reg in self._register_values] if register_name not in register_names: return None register = [ reg for reg in self._register_values if reg.name == register_name ][0] return self._register_values.get(register, None)
[docs] def unset_registers(self, registers: List[Register]): """Removes the values from registers. :param registers: List of registers :type registers: :class:`~.list` of :class:`~.Register` """ for reg in registers: self.unset_register(reg)
[docs] def unset_register(self, register: Register): """Remove the value from a register. :param register: Registers :type register: :class:`~.Register` """ assert self._register_values[register] is not None value = self._register_values[register] self._value_registers[value].remove(register) if not self._value_registers[value]: del self._value_registers[value] self._register_values[register] = None
[docs] def set_memory_value(self, mem_value: MemoryValue): """Sets a memory value. :param mem_value: Memory value to set. :type mem_value: :class:`~.MemoryValue` """ LOG.debug("Start set memory value: %s", mem_value) self.unset_memory(mem_value.address, mem_value.length) self._memory_values[mem_value.address] = mem_value if mem_value.value in self._memory_values: if mem_value not in self._value_memorys[mem_value.value]: self._value_memorys[mem_value.value].append(mem_value) LOG.debug( "Values inv %s: %s", mem_value.value, self._value_memorys[mem_value.value] ) else: LOG.debug("Already in inv dictionary") else: self._value_memorys[mem_value.value] = [mem_value] LOG.debug("Values inv %s: %s", mem_value.value, [mem_value]) assert self._memory_values[mem_value.address] == mem_value assert mem_value in self._value_memorys[mem_value.value] LOG.debug("End set memory value: %s", mem_value)
[docs] def get_memory_value(self, address: Address): """Gets a memory value. :param address: Address to look for :type address: :class:`~.Address` """ if address in self._memory_values: return self._memory_values[address] return None
[docs] def unset_memory(self, address: Address, length: int): """Unsets a memory region. :param address: Start address of the region :type address: :class:`~.Address` :param length: Length in bytes of the region :type length: :class:`~.int` """ LOG.debug("Start unset address: %s (length: %s)", address, length) possible_addresses = [ addr for addr in self._memory_values if addr.base_address == address.base_address ] # LOG.debug("Possible addresses: %s", possible_addresses) for paddr in possible_addresses: diff = paddr - address diff2 = address - paddr length2 = self._memory_values[paddr].length if ( (diff >= 0 and diff < length) or (diff2 >= 0 and diff2 < length2) ): LOG.debug("Address overlap: %s", paddr) mem_value = self._memory_values.pop(paddr) LOG.debug("Memory value: %s", mem_value) if mem_value in self._value_memorys[mem_value.value]: self._value_memorys[mem_value.value].remove(mem_value) LOG.debug("Finish unset address: %s (length: %s)", address, length)
[docs] def register_has_value(self, value: int | float | Address | str): """Returns if a value is in a register. :param value: Value to look for :type value: :class:`~.bool` """ return value in list([elem for elem in self._value_registers.keys() if type(elem) == type(value)])
[docs] def registers_get_value(self, value: int | float | Address | str): """Gets a list of registers containing the specified value. :param value: Value to look for :type value: :class:`~.int` or :class:`~.float` or :class:`~.Address` """ keyl = [key for key in self._value_registers if key == value] if len(keyl) != 1: assert keyl == 1 return self._value_registers[keyl[0]]
@property def register_values(self): """Dictionary of register, value pairs (:class:`~.dict`)""" return self._register_values @property def reserved_registers(self): """List of reserved registers (:class:`~.list`)""" return list(self._reserved_registers.values()) @property def data_segment(self): """Address starting the data segment (::class:`~.int`)""" return self._data_segment
[docs] def set_data_segment(self, value: int): """Sets the data segment start address. :param value: Start address. :type value: ::class:`~.int` """ self._data_segment = value
@property def dat(self): """DAT object (:class:`~.DynamicAddressTranslation`""" return self._dat
[docs] def set_dat(self, dat: DynamicAddressTranslation): """Sets the dynamic address translation object. :param dat: DAT object. :type dat: :class:`~.DynamicAddressTranslation` """ self._dat = dat
@property def code_segment(self): """Address starting the code segment (::class:`~.int`)""" return self._code_segment
[docs] def set_code_segment(self, value: int): """Sets the code segment start address. :param value: Start address. :type value: :class:`~.int` """ self._code_segment = value
@property def symbolic(self): """Boolean indicating if the context allows symbol labels Boolean indicating if the context allows symbol labels (:class:`~.bool`) """ return self._symbolic
[docs] def set_symbolic(self, value: bool): """Sets the symbolic property. :param value: Boolean indicating if the context allows symbol labels :type value: :class:`~.bool` """ self._symbolic = value
@property def force_absolute(self): """Boolean indicating if absolute addresses are needed. Boolean indicating if absolute addresses are needed (:class:`~.bool`) """ return self._fabsolute
[docs] def set_absolute(self, value: bool): """Sets the force_absolute property. :param value: Boolean indicating if absolute addresses are needed :type value: :class:`~.bool` """ self._fabsolute = value
# def _validate(self): # for register in self._register_values: # value = self._register_values[register] # if value is not None: # assert register in self._value_registers[value]
[docs] def dump(self): """Return a dump of the current context status. Return a dump of the current context status. Very useful for pass debugging purposes. """ mstr: List[str] = [] mstr.append("-" * 80) mstr.append("Context status:") mstr.append("Reserved Registers:") for key, value in sorted(self._reserved_registers.items()): mstr.append("Idx:\t%s\tValue:\t%s" % (key, value)) mstr.append("Registers values:") for key, value in sorted(self._register_values.items()): mstr.append("Idx:\t%s\tRaw Value:\t%s" % (key, value)) mstr.append("Registers values inverted:") for key, value in sorted([ (str(k), str(v)) for k, v in self._value_registers.items() ]): mstr.append("Idx:\t%s\tValue:\t%s" % (key, value)) mstr.append("Memory values:") for key, value in sorted(self._memory_values.items()): mstr.append("Idx:\t%s\tRaw Value:\t%s" % (key, value)) mstr.append("Memory values inverted:") for key, value in sorted(self._value_memorys.items()): mstr.append("Idx:\t%s\tValue:\t%s" % (key, value)) mstr.append("Code segment: %s" % self._code_segment) mstr.append("Data segment: %s" % self._data_segment) mstr.append("Symbolic context: %s" % self._symbolic) mstr.append("-" * 80) return "\n".join(mstr)