Source code for microprobe.code.bbl

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

"""

# Futures
from __future__ import absolute_import, annotations

from typing import TYPE_CHECKING, List

# Third party modules

# Own modules
from microprobe.code.ins import Instruction
from microprobe import MICROPROBE_RC
from microprobe.exceptions import MicroprobeCodeGenerationError, \
    MicroprobeValueError
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import Progress
from microprobe.utils.typeguard_decorator import typeguard_testsuite

# Type hinting
if TYPE_CHECKING:
    from microprobe.code.address import Address

# Constants
LOG = get_logger(__name__)
__all__ = ["Bbl", "replicate_bbls"]


# Functions
[docs] @typeguard_testsuite def replicate_bbls(bbl_list: List[Bbl], displacement: int | None = None): """Returns a copy the given basic block list at the specified displacement. :param bbl_list: List of basic blocks to copy :type bbl_list: :class:`~.list` of :class:`~.Bbl` :param displacement: Displacement of the copied basic blocks (Default value = None) :type displacement: :class:`~.int` """ bbls_replicated: List[Bbl] = [] for bbl in bbl_list: bbls_replicated.append(bbl.replicate(displacement=displacement)) return bbls_replicated
# Classes
[docs] @typeguard_testsuite class Bbl: """Class to represent a basic block."""
[docs] def __init__(self, size: int, instructions: List[Instruction] = []): """ :param size: """ if size < 1: raise MicroprobeValueError("I can not create a Bbl with %d size" % size) self._copy_id = 0 self._incstep = 10000 self._instrs: List[Instruction | None] = \ [None] * max(self._incstep, (size + 1) * 10) self._pointer = 10 self._instdic = {} self._address: Address | None = None self._displacement: int | None = 0 if MICROPROBE_RC["verbose"]: progress = Progress(size, "Initializing BBL:") if not instructions: for idx in range(0, size): # self._check_size() instr = Instruction() self._instrs[self._pointer] = instr self._instdic[instr] = self._pointer self._pointer += 10 if MICROPROBE_RC["verbose"]: progress() else: for idx in range(0, size): self._check_size() if idx < len(instructions): self._instrs[self._pointer] = instructions[idx] else: self._instrs[self._pointer] = Instruction() # Type hinting needs some help here :) instr_index: Instruction | None = self._instrs[self._pointer] assert instr_index is not None self._instdic[instr_index] = self._pointer self._pointer += 10 if MICROPROBE_RC["verbose"]: progress()
@property def instrs(self): """List of instructions in the basic block (:class:`~.list` of :class:`~.Instruction`) """ return [ elem for elem in self._instrs[0:self._pointer] if elem is not None ] @property def address(self): """Basic block address (:class:`~.Address`)""" return self._address
[docs] def set_address(self, address: Address): """Set the basic block address. :param address: Address for the basic block :type address: :class:`~.Address` """ self._address = address
@property def displacement(self): """Displacement of the basic block (::class:`~.int`)""" return self._displacement
[docs] def set_displacement(self, displacement: int | None): """Set the displacement of the basic block. :param displacement: Displacement for the basic block :type displacement: :class:`~.int` """ self._displacement = displacement
@property def size(self): """Size of the basic block, number of instructions (::class:`~.int`)""" return len(self.instrs) def _index(self, instr: Instruction | None): """Returns the index of the given instruction within the basic block. Returns the index of the given instruction within the basic block. If the instruction is not found, return a negative number. :param instr: Instruction instance :type instr: :class:`~.Instruction` """ if instr is None: return -1 else: return self._instdic.get(instr, -1)
[docs] def get_instruction_index(self, instr: Instruction): """Returns the index of the given instruction within the basic block. Returns the index of the given instruction within the basic block. If the instruction is not found, return a negative number. :param instr: Instruction instance :type instr: :class:`~.Instruction` """ return self._index(instr)
[docs] def reset_instruction(self, instr: Instruction, new_instr: Instruction): """Resets the instruction within a basic block by another instruction. Resets the instruction within a basic block by another instruction. If the instruction is not found, an exception is raised. :param instr: Instruction to replace :type instr: :class:`~.Instruction` :param new_instr: New instruction :type new_instr: :class:`~.Instruction` :raise microprobe.exceptions.MicroprobeCodeGenerationError: if the instruction is not found in the basic block """ idx = self._index(instr) if idx < 0: raise MicroprobeCodeGenerationError("Instruction not found " "in the basic block") self._instrs[idx] = new_instr
[docs] def remove_instructions_from(self, instr: Instruction): """Removes the given instruction from the basic block. Removes the given instruction from the basic block. If the instruction is not found, the basic block is not changed. :param instr: Instruction to remove :type instr: :class:`~.Instruction` """ idx = self._instdic.get(instr, -1) if idx < 0: return for linstr in [ elem for elem in self._instrs[idx:self._pointer] if elem is not None ]: self._instdic.pop(linstr) self._instrs = self._instrs[0:idx] self._pointer = idx self._check_size()
[docs] def insert_instr(self, instrs: List[Instruction], before: Instruction | None = None, after: Instruction | None = None): """Inserts a list of instruction in the basic block. Inserts a list of instruction in the basic block. Before/After parameters specify the instructions before/after which the new instruction should be added. :param instrs: Instruction to insert :type instrs: :class:`~.list` of :class:`~.Instruction` :param before: Instructions will be inserted before this instruction (Default value = None) :type before: :class:`~.list` of :class:`~.Instruction` :param after: Instructions will be inserted after this instruction (Default value = None) :type after: :class:`~.list` of :class:`~.Instruction` """ idx_before = self._index(before) idx_after = self._index(after) if idx_before == -1 and idx_after == -1: self._check_size(extra=len(instrs)) self._instrs[self._pointer:self._pointer + len(instrs)] = instrs for instr in instrs: assert instr not in self._instdic, "Something wrong" self._instdic[instr] = self._pointer self._pointer += 1 else: assert idx_before > idx_after or idx_before == -1, \ "Something wrong" # assert idx_before == -1 and idx_after > -1 if not any( self._instrs[idx_after + 1:idx_after + 1 + len(instrs)]): self._instrs[idx_after + 1:idx_after + 1 + len(instrs)] = \ instrs for instr in instrs: assert instr not in self._instdic, "Something wrong" self._instdic[instr] = idx_after + 1 idx_after += 1 else: self._check_size(extra=len(instrs)) self._instrs[(idx_after) + 1:len(instrs)] = instrs self._pointer += len(instrs) self._instdic = dict([(v, idx) for idx, v in enumerate(self._instrs) if v is not None])
def _increase(self, num: int): """Increases the basic block size by the number specified. :param num: Number of instructions to increase :type num: :class:`~.int` """ instrs: List[Instruction] = [] for _ in range(0, num): instrs.append(Instruction()) self.insert_instr(instrs) def _check_size(self, extra: int = 0): """Checks and fixes basic block size. :param extra: (Default value = 0) """ if self._pointer + extra >= len(self._instrs): self._instrs = self._instrs + [None] * self._incstep
[docs] def replicate(self, displacement: int | None = 0): """Replicates current basic block. Replicates current basic block with the given extra displacement. :param displacement: Extra displacement (Default value = 0) :type displacement: :class:`~.int` """ new_bbl = Bbl(self.size) new_bbl.set_address(self.address + displacement) new_bbl.set_displacement(displacement) for idx, orig_instr in enumerate(self.instrs): new_instr = orig_instr.copy() if new_instr.label != "": new_instr.set_label("%s_%d" % (new_instr.label, self._copy_id)) new_instr.set_address(orig_instr.address + displacement) new_bbl.reset_instruction(new_bbl.instrs[idx], new_instr) self._copy_id += 1 return new_bbl
[docs] def distance(self, instr1: Instruction, instr2: Instruction): i1 = self.get_instruction_index(instr1) i2 = self.get_instruction_index(instr2) idxs = list(sorted([i1, i2])) distance = [ elem for elem in sorted(self._instdic.values()) if elem > idxs[0] and elem <= idxs[1] ] return len(distance)
[docs] def get_instruction_by_distance(self, instr: Instruction, distance: int): i1 = self.get_instruction_index(instr) idx = sorted(self._instdic.values()).index(i1) if idx - distance >= 0: idx = idx + distance else: idx = 0 idx = sorted(self._instdic.values())[idx] return self._instrs[idx]