Ejemplo n.º 1
0
 def __init__(self):
     """Initialization abstract method."""
     self._bench = None
     self._target = None
     self._context = Context()
     self._reset_state = False
     self._direct_init_dict = None
Ejemplo n.º 2
0
    def context(self):
        """ """

        context = Context()
        context.set_code_segment(self._init_code_address)
        context.set_data_segment(self._init_data_address)
        context.set_symbolic(False)
        context.set_absolute(True)

        return context
Ejemplo n.º 3
0
    def context(self):
        """ """

        context = Context()
        context.set_code_segment(0x100000)
        context.set_data_segment(0x200000)
        context.set_symbolic(False)
        return context
Ejemplo n.º 4
0
    def elf_abi(self, stack_size, start_symbol, **kwargs):
        """ """

        stack = VariableArray(kwargs.get("stack_name", "microprobe_stack"),
                              "uint8_t",
                              stack_size,
                              align=kwargs.get("stack_alignment", 16),
                              address=kwargs.get("stack_address", None))

        instructions = []
        instructions += self.target.set_register_to_address(
            self.stack_pointer,
            Address(base_address=kwargs.get("stack_name", "microprobe_stack")),
            Context())

        if self.stack_direction == "decrease":
            instructions += self.target.add_to_register(
                self.stack_pointer, stack_size)

        if start_symbol is not None:
            instructions += self.target.function_call(start_symbol)
            instructions += self.target.function_call("ELF_ABI_EXIT")

        instructions[0].set_label("ELF_ABI_START")

        return [stack], instructions
Ejemplo n.º 5
0
    def get_context(self, variable=None, tmpl_path=None):
        """ """

        if variable is None:
            variable = self.context_var

        if variable.size < self.context_var.size:
            raise MicroprobeCodeGenerationError(
                "Variable '%s' is too small to save the target context")

        asm = open(os.path.join(tmpl_path, "getcontext.S")).readlines()

        if len(asm) == 0:
            return []

        reg = self._scratch_registers[0]
        instrs = self.set_register_to_address(reg, variable.address, Context())

        return instrs + \
            microprobe.code.ins.instructions_from_asm(asm, self.target)
Ejemplo n.º 6
0
    def function_call(self, target, return_address_reg=None, long_jump=False):

        if return_address_reg is None:
            return_address_reg = self.target.isa.registers["X1"]

        if isinstance(target, str):
            target = InstructionAddress(base_address=target)

        if long_jump:
            assert isinstance(target, int)
            instrs = self.target.set_register(return_address_reg, target,
                                              Context())

            jalr_ins = self.target.new_instruction("JALR_V0")
            jalr_ins.set_operands([0, return_address_reg, return_address_reg])
            jalr_ins.add_comment("Long jump to address 0X%016X" % target)

            instrs.append(jalr_ins)
            return instrs

        else:
            jal_ins = self.target.new_instruction("JAL_V0")
            jal_ins.set_operands([target, return_address_reg])
            return [jal_ins]
Ejemplo n.º 7
0
def _compute_reset_code(target, test_def, args):
    instructions = interpret_asm(
        test_def.code, target, [var.name for var in test_def.variables],
        show_progress=True,
    )

    # TODO: This can be done in parallel or look for speed up the process
    instructions = [
        instruction_from_definition(instr) for instr in instructions
    ]

    instruction_dict = {}
    address = test_def.default_code_address
    progress = Progress(
        len(test_def.roi_memory_access_trace),
        msg="Building instruction dictionary",
    )
    for instr in instructions:
        progress()
        if instr.address is not None:
            if instr.address.base_address == "code":
                address = test_def.default_code_address + \
                          instr.address.displacement
                instr.set_address(address)
        else:
            address = address + instr.architecture_type.format.length
            instr.set_address(address)
        instruction_dict[instr.address] = instr

    free_regs = []
    written_after_read_regs = []
    read_regs = []
    level = 0
    dynamic_count = 0
    progress = Progress(
        len(test_def.roi_memory_access_trace),
        msg="Evaluating register usage",
    )
    reset_regs = set()
    for access in test_def.roi_memory_access_trace:
        progress()

        if access.data_type == "D":
            continue

        dynamic_count += 1
        try:
            instr = instruction_dict[access.address]
            uses = instruction_dict[access.address].uses()
            sets = instruction_dict[access.address].sets()

        except KeyError:
            print_error(
                "Access to from instruction at address "
                "0x%016X registered but such instruction is not"
                " present in the definition." % access.address,
            )
            exit(1)

        # Calls
        if instr.mnemonic == "BL":
            level += 1
        elif instr.mnemonic == "BCL":
            level += 1
        elif instr.mnemonic == "BCCTRL":
            if instr.operands()[2].value in [0, 3]:
                level += 1

        # Returns
        if instr.mnemonic == "BCLR":
            if (((instr.operands()[0].value & 0b10100) == 20) and
                    (instr.operands()[2].value == 0)):
                level -= 1

        # TODO: this should include Z and RISCV instructions for call
        # and return, but currently we do not have memory access traces
        # for such platforms

        for reg in uses:
            if reg not in read_regs:
                read_regs.append(reg)

        for reg in sets:
            if reg in free_regs:
                continue
            elif reg not in read_regs:
                free_regs.append(reg)
            elif reg not in written_after_read_regs:
                written_after_read_regs.append(reg)

        reset_regs = set(read_regs).intersection(
            set(written_after_read_regs),
        )

    reset_regs = sorted(reset_regs)

    assert len(free_regs) == len(set(free_regs))
    assert len(set(free_regs).intersection(set(reset_regs))) == 0

    if len(test_def.roi_memory_access_trace) == 0:
        # We do not have memory access trace, assume calling conventions
        reset_regs = target.volatile_registers

    reset_regs = [
        reg for reg in reset_regs if reg in target.volatile_registers]

    if len(reset_regs) == 0 and len(test_def.roi_memory_access_trace) == 0:
        print_info(
            "No memory access trace found. Resetting volatile registers."
        )
        reset_regs = target.volatile_registers

    unused_regs = sorted(
        (reg for reg in target.registers.values() if reg not in read_regs),
    )

    #
    # Make sure scratch registers are reset last
    #
    for reg in target.scratch_registers:
        if reg in reset_regs:
            reset_regs.remove(reg)
            reset_regs.append(reg)

    free_regs = unused_regs + free_regs

    # Know which ones are not used (or written) and which ones are used
    # Use them as base / temporal registers for addresses

    # Check addresses
    conflict_addresses = {}
    new_ins = []
    progress = Progress(
        len(test_def.roi_memory_access_trace),
        msg="Evaluating memory usage",
    )
    for access in test_def.roi_memory_access_trace:
        progress()
        if access.data_type == "I":
            continue
        val = conflict_addresses.get(
            access.address,
            [access.length, access.access_type],
        )
        if access.access_type not in val[1]:
            val[1] += access.access_type
        val[0] = max(val[0], access.length)
        conflict_addresses[access.address] = val

    fix_addresses = []
    for address in conflict_addresses:
        value = conflict_addresses[address]
        if value[1] == "RW":
            wvalue = None
            for var in test_def.variables:
                if var.var_type.upper() in ["CHAR", "UINT8_T"]:
                    elem_size = 1
                else:
                    raise NotImplementedError
                end_address = var.address + var.num_elements * elem_size
                if var.address <= address <= end_address:
                    offset = int((address - var.address) / elem_size)
                    svalue = var.init_value[
                        offset:offset + int(value[0] / elem_size)
                    ]
                    svalue = "".join(["%02X" % tval for tval in svalue])
                    wvalue = int(svalue, 16)
                    break

            if wvalue is None:
                print_error(
                    "Unable to restore original value for address 0x%X" %
                    address,
                )
                exit(1)

            if value[0] <= 8:
                fix_addresses.append((address, value[0], wvalue))
            else:
                for selem in range(0, value[0]//8):
                    sfmt = "%%0%dX" % (2*value[0])
                    nvalue = sfmt % wvalue
                    nvalue = int(nvalue[selem*16:(selem+1)*16], 16)
                    fix_addresses.append(
                        (address + selem * 8,
                         8,
                         nvalue)
                    )

    reset_steps = []

    context = Context()
    context.set_symbolic(True)

    if len(fix_addresses) > 0:

        # TODO: This can be optimized. Reduce the number of instructions to
        # be added by sorting the reset code (shared values or similar
        # addresses)
        # TODO: This can be optimized for use vector registers when
        # needed
        #
        print_info("Adding instructions to reset memory state")
        reset_register = [
            reg
            for reg in free_regs
            if reg.type.used_for_address_arithmetic and
            reg.name != "GPR0"
        ][0]

        for address, length, value in fix_addresses:

            address_obj = Address(base_address="data", displacement=address)
            new_instructions = target.set_register(
                reset_register, value, context, opt=False,
            )

            for ins in new_instructions:
                ins.add_comment(
                    "Reset code. Setting %s to 0X%016X" %
                    (reset_register.name, value),
                )

            reset_steps.append([new_instructions[:], reset_register, value])
            context.set_register_value(reset_register, value)

            try:
                store_ins = target.store_integer(
                    reset_register, address_obj, length * 8, context,
                )
                new_instructions += store_ins
                reset_steps.append(
                    [store_ins, reset_register, address_obj, length],
                )

            except MicroprobeCodeGenerationError:
                areg = [
                    reg for reg in free_regs
                    if reg.type.used_for_address_arithmetic and reg.name !=
                    "GPR0"
                ][1]

                set_ins = target.set_register(
                    areg, address, context, opt=False,
                )
                new_instructions += set_ins
                reset_steps.append([set_ins, areg, address_obj])

                context.set_register_value(areg, address_obj)

                store_ins = target.store_integer(
                    reset_register, address_obj, length * 8, context,
                )
                new_instructions += store_ins
                reset_steps.append(
                    [store_ins, reset_register, address_obj, length],
                )

                for ins in set_ins:
                    ins.add_comment(
                        "Reset code. Setting %s to 0X%016X" %
                        (areg.name, address),
                    )

            for ins in store_ins:
                ins.add_comment(
                    "Reset code. Setting mem content in 0X%016X" % (address),
                    )

            new_ins.extend(new_instructions)

    # Reset contents of used registers
    for reset_register in reset_regs:
        try:
            value = [
                reg for reg in test_def.registers if reg.name ==
                reset_register.name
            ][0].value
        except IndexError:
            continue

        new_instructions = target.set_register(
            reset_register, value, context, opt=False,
        )
        reset_steps.append([new_instructions, reset_register, value])
        context.set_register_value(reset_register, value)

        for ins in new_instructions:
            ins.add_comment(
                "Reset code. Setting %s to 0X%016X" %
                (reset_register.name, value),
            )

        new_ins.extend(new_instructions)

    try:
        overhead = (((len(new_ins) * 1.0) / dynamic_count) * 100)
    except ZeroDivisionError:
        print_warning("Unable to compute overhead. Zero dynamic instruction "
                      "count")
        overhead = 0

    print_info(
        "%03.2f%% overhead added by resetting code" % overhead,
    )
    if overhead > args['wrap_endless_threshold']:
        print_error(
            "Instructions added: %d" % len(new_ins),
        )
        print_error(
            "Total instructions: %d" % dynamic_count,
        )
        print_error(
            "Reset code above --wrap-endless-threshold. Stopping generation.",
        )
        exit(1)

    return new_ins, overhead, reset_steps
Ejemplo n.º 8
0
class Wrapper(six.with_metaclass(abc.ABCMeta, object)):
    """
    Abstract class to represent a language wrapper.
    """
    @abc.abstractmethod
    def __init__(self):
        """Initialization abstract method."""
        self._bench = None
        self._target = None
        self._context = Context()
        self._reset_state = False

    @abc.abstractmethod
    def outputname(self, name):
        """

        :param name:

        """
        raise NotImplementedError

    @abc.abstractmethod
    def headers(self):
        """ """
        raise NotImplementedError

    # @abc.abstractmethod
    # def declare_option(self, option_flag, var,):
    #    raise NotImplementedError

    @abc.abstractmethod
    def declare_global_var(self, var):
        """

        :param var:

        """
        raise NotImplementedError

    @abc.abstractmethod
    def init_global_var(self, var, value):
        """

        :param var:
        :param value:

        """
        raise NotImplementedError

    @abc.abstractmethod
    def required_global_vars(self):
        """ """
        raise NotImplementedError

    @abc.abstractmethod
    def start_main(self):
        """ """
        raise NotImplementedError

    @abc.abstractmethod
    def post_var(self):
        """ """
        raise NotImplementedError

    @abc.abstractmethod
    def start_loop(self, instr, instr_reset, aligned=True):
        """

        :param instr:
        :param instr_reset:
        :param aligned:  (Default value = True)

        """
        raise NotImplementedError

    @abc.abstractmethod
    def wrap_ins(self, instr):
        """

        :param instr:

        """
        raise NotImplementedError

    @abc.abstractmethod
    def end_loop(self, instr):
        """

        :param instr:

        """
        raise NotImplementedError

    @abc.abstractmethod
    def end_main(self):
        """ """
        raise NotImplementedError

    @abc.abstractmethod
    def footer(self):
        """ """
        raise NotImplementedError

    @abc.abstractmethod
    def infinite(self):
        """Returns a :class:`~.bool` indicating if the loop is infinite. """
        raise NotImplementedError

    @abc.abstractmethod
    def reserved_registers(self, registers, target):
        """

        :param registers:
        :param target:

        """
        raise NotImplementedError

    def set_benchmark(self, bench):
        """

        :param bench:

        """
        self._bench = bench

    @property
    def benchmark(self):
        """ """
        return self._bench

    @property
    def reset(self):
        """ """
        return self._reset_state

    def set_target(self, target):
        """

        :param target:

        """
        self._target = target

    @property
    def target(self):
        """ """
        return self._target

    def context(self):
        """ """
        return self._context.copy()

    def init_loop_pad(self):
        """ """
        return 0

    def init_main_pad(self):
        """ """
        return 0

    @property
    def direct_initialization_support(self):
        """ Boolean indicating if the wrapper supports direct initialization.

        Direct initialization refers to the capability of initializing values
        without requiring the execution of instructions. For instance,
        simulation-based format usually allow the specification of the
        initial values of the memory and the registers.
        """
        return False

    def register_direct_init(self, dummy_key, dummy_value):
        """ Initialize *key* with the value *value* """
        if self.direct_initialization_support:
            raise NotImplementedError
        else:
            raise MicroprobeCodeGenerationError(
                "Direct intialization function called but not supported")
Ejemplo n.º 9
0
def _shift_and_fix_code(
    target, code, offset, addresses, reset_steps, registers,
        ):
    """Shift code and fix reset code."""
    scode = []
    for instruction in code:
        instruction = instruction.copy()
        # Fix and shift decorators (not need to modify code)
        for key, values in instruction.decorators.items():
            if not isinstance(values, list):
                values = [values]
            if key in ['MA', 'BT']:
                for idx in range(0, len(values)):
                    if addresses[0] <= values[idx] <= addresses[1]:
                        values[idx] = values[idx] + offset
        scode.append(instruction)

    cidx = 0
    context = Context()
    context.set_symbolic(False)

    for reset_step in reset_steps:

        rins = reset_step[0]
        cins = scode[cidx:cidx+len(rins)]
        rreg = reset_step[1]

        try:
            rval = [reg for reg in registers if reg.name == rreg.name][0].value
        except IndexError:
            # This was a support register to compute an address,
            # it should be fixed
            rval = 0

        for rin, cin in zip(rins, cins):
            if rin.name != cin.instruction_type.name:
                print_error("Unable to fix the reset code")
                exit(1)

        if len(reset_step) == 3:
            rins, reset_register, value = reset_step
            if not isinstance(value, six.integer_types):
                # All addresses should be offset
                value += offset
                nins = target.set_register(
                    reset_register,
                    value.displacement,
                    context,
                    opt=False,
                )
                print_info(
                    "Fixing reset code for reg: %s. "
                    "New value: 0x%016X" %
                    (rreg.name, value.displacement),
                )
            else:
                if abs(value-rval) == offset:
                    # This has been shifted, force offset
                    value += offset
                    print_info(
                        "Fixing reset code for reg: %s. "
                        "New value: 0x%016X" %
                        (rreg.name, value),
                    )
                nins = target.set_register(
                    reset_register, value, context, opt=False,
                )

            context.set_register_value(reset_register, value)
        elif len(reset_step) == 4:
            rins, reset_register, address_obj, length = reset_step
            # All addresses should be offsetted
            address_obj += offset
            nins = target.store_integer(
                reset_register, address_obj, length * 8, context,
            )
            print_info(
                "Fixing reset code for reg: %s. "
                "New value: 0x%016X" %
                (rreg.name, address_obj.displacement),
            )
        else:
            raise NotImplementedError(
                "Unable to shift and fix code"
            )

        if len(rins) != len(nins):
            print_error("Original resetting code:")
            for ins in rins:
                print_error(ins.assembly())
            print_error("New resetting code:")
            for ins in nins:
                print_error(ins.assembly())
            print_error("New resetting code differs from original in length")
            exit(1)

        for ins in nins:
            if len(reset_step) == 3:
                if not isinstance(value, six.integer_types):
                    value = value.displacement
                ins.add_comment(
                    "Reset code. Setting %s to 0X%016X" %
                    (reset_register.name, value),
                )
            else:
                ins.add_comment(
                    "Reset code. Setting mem content in 0X%016X" %
                    (address_obj.displacement),
                )

        for idx, (nin, cin) in enumerate(zip(nins, cins)):
            if nin.name != cin.instruction_type.name:
                print_warning("New code differs from original in opcodes")
            scode[cidx+idx] = instruction_to_definition(nin)
            scode[cidx+idx].comments = scode[cidx+idx].comments[1:]

        cidx += len(rins)

    return scode