# 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.passes.structure` module
"""
# Futures
from __future__ import absolute_import, division
# Built-in modules
# Third party modules
# Own modules
import microprobe.passes
from microprobe.code.address import InstructionAddress
from microprobe.code.bbl import replicate_bbls
from microprobe.utils.logger import get_logger
# Local modules
# Constants
LOG = get_logger(__name__)
__all__ = ['SimpleBuildingBlockPass', 'GenericCodeStructurePass']
# Functions
# Classes
[docs]
class SimpleBuildingBlockPass(microprobe.passes.Pass):
"""SimpleBuildingBlockPass Class.
This :class:`~.Pass` adds a single building block of a given instruction
size to the given building block. The pass fails if the buiding block
size differs in ratio more than the threshold provided.
"""
[docs]
def __init__(self, bblsize, threshold=0.1):
"""Create a SimpleBuildingBlockPass object.
:param bblsize: Size of the building block to add
:type bblsize: :class:`int`
:param threshold: Allowed deviation from the size provided
(Default value = 0.1)
:type threshold: :class:`float`
:return: A new SimpleBuildingBlockPass object.
:rtype: :class:`SimpleBuildingBlockPass`
"""
super(SimpleBuildingBlockPass, self).__init__()
self._bblsize = bblsize
self._description = "Create a basic block with '%d' " \
"instructions" % self._bblsize
self._threshold = threshold
def __call__(self, building_block, dummy):
"""
:param building_block:
:param dummy:
"""
building_block.cfg.add_bbl(size=self._bblsize)
[docs]
def check(self, building_block, dummy_target):
"""
:param building_block:
:param dummy_target:
"""
pass_ok = True
pass_ok = pass_ok and (len(building_block.cfg.bbls) == 1)
for bbl in building_block.cfg.bbls:
LOG.debug("BBL size: %d", bbl.size)
LOG.debug("Expected BBL size: %d", self._bblsize)
if (abs((bbl.size * 1.0) / self._bblsize) - 1) > self._threshold:
LOG.warning(
"Percentage deviation: %.2f", abs(
(bbl.size * 1.0) / self._bblsize
) - 1
)
pass_ok = False
return pass_ok
[docs]
class GenericCodeStructurePass(microprobe.passes.Pass):
""" """
[docs]
def __init__(self, model):
"""
:param model:
"""
super(GenericCodeStructurePass, self).__init__()
self._model = model
def __call__(self, building_block, target):
"""
:param building_block:
:param target:
"""
descriptors = self._model(building_block.code_size)
current_displacement = 0
orig_bbls = building_block.cfg.bbls[:]
iteration_register = None
first = True
for idx, elem in enumerate(descriptors):
chunks, displacement, iterations = elem
LOG.debug("Descriptor info:")
LOG.debug(" Chunks: %d", chunks)
LOG.debug(" Displacement: %d", displacement)
LOG.debug(" Iterations: %d", iterations)
if chunks < 1 or iterations < 1:
continue
for dummy in range(0, iterations):
first_chunk_in_iteration = None
last_chunk_in_iteration = None
for dummy in range(0, chunks):
if first:
LOG.debug("First Chunk - No copy")
first = False
current_chunk = orig_bbls
# prev_chunk = None
else:
LOG.debug("Other Chunk - Replicating")
# prev_chunk = current_chunk
current_chunk = replicate_bbls(
orig_bbls,
displacement=current_displacement
)
building_block.cfg.add_bbls(current_chunk)
if first_chunk_in_iteration is None:
first_chunk_in_iteration = current_chunk
last_chunk_in_iteration = current_chunk
current_displacement = current_displacement + displacement
first_chunk_in_iteration = first_chunk_in_iteration[0]
last_chunk_in_iteration = last_chunk_in_iteration[-1]
if iterations > 1:
LOG.debug("Adding loop control instructions")
if iteration_register is None:
iteration_register = \
target.get_register_for_address_arithmetic(
building_block.context)
building_block.context.add_reserved_registers(
[iteration_register]
)
LOG.debug(
"Using register '%s' as loop control "
"register", iteration_register
)
# set the number of iteration to the first
newins = target.set_register(
iteration_register, 0, building_block.context
)
# pylint: disable=E1103
first_chunk_in_iteration.insert_instr(
newins, before=first_chunk_in_iteration.instrs[0]
)
# pylint: enable=E1103
# set the branch to the last
label = "chunk_%d" % (idx)
newins = target.add_to_register(iteration_register, 1)
newins += target.compare_and_branch(
iteration_register, iterations, "<", label,
building_block.context
)
# pylint: disable=E1103
first_chunk_in_iteration.instrs[0].setlabel(label)
last_chunk_in_iteration.insert_instr(
newins, after=last_chunk_in_iteration.instrs[-1]
)
# pylint: enable=E1103
# link_BBLS
prev_bbl = None
for bbl in building_block.cfg.bbls:
if prev_bbl is not None:
LOG.debug("Linking with previous chunk")
source = InstructionAddress(
base_address="code",
displacement=prev_bbl.instrs[-1].address.displacement +
prev_bbl.instrs[-1].architecture_type.format.length
)
newins = target.branch_unconditional_relative(
source, bbl.instrs[0]
)
prev_bbl.insert_instr([newins], after=prev_bbl.instrs[-1])
prev_bbl = bbl
# exit(0)
[docs]
def check(self, building_block, target):
"""
:param building_block:
:param target:
"""
raise NotImplementedError