# 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.initialization` module
"""
# Futures
from __future__ import absolute_import, print_function
# Third party modules
# Own modules
import microprobe.code
import microprobe.passes
from microprobe.code.ins import instruction_set_def_properties
from microprobe.exceptions import (
MicroprobeCodeGenerationError,
MicroprobeDuplicatedValueError,
)
from microprobe.utils.asm import interpret_asm
from microprobe.utils.cmdline import print_warning
from microprobe.utils.ieee import ieee_float_to_int64
from microprobe.utils.logger import get_logger
# Constants
LOG = get_logger(__name__)
__all__ = [
"AutoAlignPass",
"AddInitializationAssemblyPass",
"AddFinalizationAssemblyPass",
"AddInitializationInstructionsPass",
"InitializeRegistersPass",
"InitializeRegisterPass",
"ReserveRegistersPass",
"UnReserveRegistersPass",
]
# Functions
# Classes
[docs]
class AddInitializationAssemblyPass(microprobe.passes.Pass):
"""AddInitializationAssemblyPass pass."""
[docs]
def __init__(self, assembly, allow_registers=None):
"""
:param assembly:
:type assembly:
"""
super().__init__()
self._asm = assembly
self._aregs = allow_registers
self._description = (
"Append the '%s' instructions at the init of the"
"building block." % ";".join(assembly)
)
def __call__(self, building_block, target):
"""
:param building_block:
:param target:
"""
instructions_def = interpret_asm(
self._asm.split("\n"), target, building_block.labels
)
instructions = []
for definition in instructions_def:
instruction = microprobe.code.ins.Instruction()
instruction_set_def_properties(
instruction,
definition,
building_block=building_block,
target=target,
allowed_registers=self._aregs,
)
instructions.append(instruction)
building_block.add_init(instructions)
[docs]
class AddFinalizationAssemblyPass(microprobe.passes.Pass):
"""AddFinalizationAssemblyPass pass."""
[docs]
def __init__(self, assembly, allow_registers=None):
"""
:param assembly:
:type assembly:
"""
super().__init__()
self._asm = assembly
self._aregs = allow_registers
self._description = (
"Append the '%s' instructions at the init of the"
"building block." % ";".join(assembly)
)
def __call__(self, building_block, target):
"""
:param building_block:
:param target:
"""
instructions_def = interpret_asm(
self._asm.split("\n"), target, building_block.labels
)
instructions = []
for definition in instructions_def:
instruction = microprobe.code.ins.Instruction()
instruction_set_def_properties(
instruction,
definition,
building_block=building_block,
target=target,
allowed_registers=self._aregs,
)
instructions.append(instruction)
building_block.add_fini(instructions)
[docs]
class ReserveRegistersPass(microprobe.passes.Pass):
"""ReserveRegistersPass pass."""
[docs]
def __init__(self, register_names):
"""
:param register_names:
:type register_names:
"""
super().__init__()
self._register_names = register_names
self._description = f"Reserve registers '{self._register_names}'"
def __call__(self, building_block, target):
for register_name in self._register_names:
try:
register = target.registers[register_name]
except KeyError as exc:
raise MicroprobeCodeGenerationError(
"Unknown register '%s'. Known registers: %s"
% (register_name, list(target.registers.keys()))
) from exc
building_block.context.add_reserved_registers([register])
[docs]
class UnReserveRegistersPass(microprobe.passes.Pass):
"""UnReserveRegistersPass pass."""
[docs]
def __init__(self, register_names):
"""
:param register_names:
:type register_names:
"""
super().__init__()
self._register_names = register_names
self._description = f"Reserve registers '{self._register_names}'"
def __call__(self, building_block, target):
for register_name in self._register_names:
try:
register = target.registers[register_name]
except KeyError as exc:
raise MicroprobeCodeGenerationError(
"Unknown register '%s'. Known registers: %s"
% (register_name, list(target.registers.keys()))
) from exc
building_block.context.remove_reserved_registers([register])
[docs]
class InitializeRegistersPass(microprobe.passes.Pass):
"""InitializeRegistersPass pass."""
[docs]
def __init__(self, *args, **kwargs):
"""
:param value: (Default value = None)
:param fp_value: (Default value = None)
:param v_value: (Default value = None)
"""
super().__init__()
value = kwargs.get("value", None)
fp_value = kwargs.get("fp_value", None)
v_value = kwargs.get("v_value", None)
skip_unknown = kwargs.get("skip_unknown", False)
warn_unknown = kwargs.get("warn_unknown", False)
self._force_code = kwargs.get("force_code", False)
if len(args) == 1:
self._reg_dict = dict(
[(elem.name, elem.value) for elem in args[0]]
)
self._priolist = [elem.name for elem in args[0]]
else:
self._reg_dict = {}
self._priolist = []
self._value = value
self._fp_value = fp_value
self._vect_value = None
self._vect_elemsize = None
self._skip_unknown = skip_unknown
self._warn_unknown = warn_unknown
self._force_reserved = kwargs.get("force_reserved", False)
self._skip_control = kwargs.get("skip_control", False)
if v_value is not None:
self._vect_value, self._vect_elemsize = v_value
self._description = (
"Initialize general registers to: "
" '%s' and FP registers to '%s' and"
" Vector register to '%s' "
% (self._value, self._fp_value, v_value)
)
def __call__(self, building_block, target):
"""
:param building_block:
:param target:
"""
if not self._skip_unknown:
for register_name in self._reg_dict:
if register_name not in list(target.registers.keys()):
raise MicroprobeCodeGenerationError(
f"Unknown register name: '{register_name}'. "
"Unable to set it"
)
if self._warn_unknown:
for register_name in self._reg_dict:
if register_name not in list(target.registers.keys()):
print_warning(
f"Unknown register name: '{register_name}'. "
"Unable to set it"
)
regs = sorted(
target.registers.values(),
key=lambda x: self._priolist.index(x.name)
if x.name in self._priolist
else 314159,
)
#
# Make sure scratch registers are set last
#
for reg in target.scratch_registers:
if reg in regs:
regs.remove(reg)
regs.append(reg)
for reg in regs:
value = None
elemsize = None
force_direct = False
if reg.name in self._reg_dict:
value = self._reg_dict[reg.name]
self._reg_dict.pop(reg.name)
force_direct = True
if (
reg in building_block.context.reserved_registers
and not self._force_reserved
):
LOG.debug("Skip reserved - %s", reg)
continue
if reg in target.control_registers and (
value is None or self._skip_control
):
LOG.debug("Skip control - %s", reg)
continue
if value is None:
if reg.used_for_vector_arithmetic:
if self._vect_value is not None:
value = self._vect_value
elemsize = self._vect_elemsize
else:
LOG.debug(
"Skip no vector default value provided - %s", reg
)
continue
elif reg.used_for_float_arithmetic:
if self._fp_value is not None:
value = self._fp_value
else:
LOG.debug(
"Skip no float default value provided - %s", reg
)
continue
else:
if self._value is not None:
value = self._value
else:
LOG.debug("Skip no default value provided - %s", reg)
continue
while callable(value):
value = value()
if isinstance(value, int):
value = value & ((2**reg.size) - 1)
if reg.used_for_float_arithmetic:
value = ieee_float_to_int64(float(value))
elif reg.used_for_vector_arithmetic:
if isinstance(value, float):
if elemsize != 64:
raise MicroprobeCodeGenerationError(
"Unable to initialize '%s' to '%s'. Only 64bit"
" vector element initialization is supported"
% (reg.name, (value, elemsize))
)
value = ieee_float_to_int64(float(value))
value = "%d_%d" % (value, elemsize)
else:
value = "%d_%d" % (value, elemsize)
LOG.debug("Setting reg %s to val %s", reg, value)
if (
target.wrapper.direct_initialization_support
and not self._force_code
):
try:
target.wrapper.register_direct_init(
reg, value, force=force_direct
)
if isinstance(value, str):
LOG.debug("Direct set of '%s' to '%s'", reg, value)
else:
LOG.debug("Direct set of '%s' to '0x%x'", reg, value)
except MicroprobeCodeGenerationError:
building_block.add_init(
target.set_register(reg, value, building_block.context)
)
LOG.debug("Set '%s' to '0x%x'", reg, value)
except MicroprobeDuplicatedValueError:
LOG.debug("Skip already set - %s", reg)
else:
building_block.add_init(
target.set_register(reg, value, building_block.context)
)
building_block.context.set_register_value(reg, value)
[docs]
def check(self, building_block, target):
"""
:param building_block:
:param target:
"""
raise NotImplementedError
[docs]
class InitializeRegisterPass(microprobe.passes.Pass):
"""InitializeRegisterPass pass."""
[docs]
def __init__(
self,
register_name,
value,
reserve=None,
force=False,
force_code=False,
force_value=False,
force_control=False,
):
"""
:param register_name:
:param value:
:param reserve: (Default value = False)
"""
super().__init__()
self._value = value
self._register_name = register_name
self._reserve = reserve
self._force = force
self._force_code = force_code
self._force_value = force_value
self._force_control = force_control
self._description = "Initialize register '%s' to " "'%s'" % (
self._register_name,
self._value,
)
def __call__(self, building_block, target):
"""
:param building_block:
:param target:
"""
reg = [
reg
for reg in target.registers.values()
if reg.name == self._register_name
][0]
value = self._value
if reg in building_block.context.reserved_registers and self._reserve:
raise MicroprobeCodeGenerationError(
f"Register '{str(reg)}' already reserved"
)
if reg in target.control_registers and self._force_control is False:
raise MicroprobeCodeGenerationError(
f"Register '{str(reg)}' in Target definition control registers"
)
if callable(value):
value = value()
if reg.used_for_float_arithmetic and not self._force_value:
value = ieee_float_to_int64(float(value))
if (
target.wrapper.direct_initialization_support
and not self._force_code
):
target.wrapper.register_direct_init(reg, value, force=self._force)
else:
building_block.add_init(
target.set_register(reg, value, building_block.context)
)
building_block.context.set_register_value(reg, value)
if (
self._reserve is not False
and reg not in building_block.context.reserved_registers
):
building_block.context.add_reserved_registers([reg])
[docs]
def check(self, building_block, target):
"""
:param building_block:
:param target:
"""
raise NotImplementedError
[docs]
class AddInitializationInstructionsPass(microprobe.passes.Pass):
"""AddInitializationInstructionsPass pass."""
[docs]
def __init__(self, instr, operands=None):
"""
:param instr:
:param operands:
"""
super().__init__()
self._instr = instr
self._operands = operands
self._description = (
f"Add {instr} in the init sequence with operands: {operands}"
)
# TODO: improve description string
def __call__(self, building_block, dummy_target):
"""
:param building_block:
:param dummy_target:
"""
if self._operands is not None:
for instr, operands in zip(self._instr, self._operands):
newinstr = microprobe.code.ins.Instruction()
# print instr
# print self._instr
newinstr.set_arch_type(instr)
newinstr.set_operands(operands)
# print operands
building_block.add_init([newinstr])
else:
for instr in self._instr:
building_block.add_init([instr])
[docs]
class AutoAlignPass(microprobe.passes.Pass):
"""AutoAlignPass pass."""
[docs]
def __init__(self, instr, operands, mod):
"""
:param instr:
:type instr:
:param operands:
:type operands:
:param mod:
:type mod:
"""
super().__init__()
self._instr = instr
self._operands = operands
self._mod = mod
self._description = (
"Align the loop to be module '%s' using '%s'"
" instruction with '%s' operands" % (mod, instr, operands)
)
def __call__(self, building_block, dummy_target):
"""
:param building_block:
:param dummy_target:
"""
displacement = (
building_block.init[-1].address.displacement
+ building_block.init[-1].format.length
)
while ((displacement) % self._mod) != 0:
for instr, operands in zip(self._instr, self._operands):
newinstr = microprobe.code.ins.Instruction()
newinstr.set_arch_type(instr)
newinstr.set_operands(operands)
if ((displacement) % self._mod) == 0:
break
displacement += newinstr.format.length
building_block.add_init([newinstr])