def get_direct_init(self, key, defaultvalue): """ Get the *value* for *key* """ if self.direct_initialization_support: if isinstance(key, str): keys = self.target.registers.values() keys = [lkey for lkey in keys if lkey.name == key] if len(keys) != 1: raise MicroprobeCodeGenerationError( "Unable to find the direct initialization value" " name: %s" % key) key = keys[0] if key in self._direct_init_dict: return self._direct_init_dict[key] if defaultvalue is not None: return defaultvalue raise MicroprobeCodeGenerationError( "Unable to find the direct initialization value") else: raise MicroprobeCodeGenerationError( "Direct intialization function called but not supported")
def __init__(self, name, vartype, align=1, value=None, address=None): """ :param name: :param vartype: :param align: (Default value = 16) :param value: (Default value = None) """ super(VariableSingle, self).__init__() self._name = name self._type = vartype.lower() self._align = align self._value = value self._address = address if align is not None: if align <= 0: raise MicroprobeCodeGenerationError( "Alignment should be > 0 in definition of " "variable: '%s'" % name) val = math.log(align, 2) if val != int(val): raise MicroprobeCodeGenerationError( "Alignment should be power of 2 in definition of " "variable: '%s'" % name)
def get_register_for_address_arithmetic(self, context): """ :param context: """ reg = [ reg for reg in self._address_registers if reg not in context.reserved_registers and int(reg.codification) >= 8 and int(reg.codification) <= 15 ] reg += [ reg for reg in self._address_registers if reg not in context.reserved_registers and int(reg.codification) < 8 and int(reg.codification) > 15 ] if len(reg) == 0: raise MicroprobeCodeGenerationError( "No free registers available. " "Change your policy." ) return reg[0]
def __init__(self, instrs, prepend=None, allow_registers=None): """ :param instrs: """ super(SetInstructionTypeBySequencePass, self).__init__() for instr in instrs: if not isinstance(instr, InstructionType): raise MicroprobeCodeGenerationError( "Invalid sequence definition. '%s' is not an instruction" " type " % instr) self._sequence = instrs self._func = getnextf(itertools.cycle(instrs)) self._description = "Repeat the instruction sequence '%s'" % \ ([instruction.name for instruction in self._sequence]) if prepend is None: prepend = [] self._prepend = prepend self._aregs = [] if allow_registers is not None: self._aregs = allow_registers
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")
def __add__(self, other): """ :param other: """ if isinstance(other, self.__class__): if self.base_address != other.base_address: raise MicroprobeCodeGenerationError( "I can not add '%s' " "and '%s'" % (self, other) ) return self.__class__( self.base_address, self.displacement + other.displacement ) elif isinstance(other, int): return self.__class__(self.base_address, self.displacement + other) else: raise NotImplementedError
def representation(self, value): """ :param value: """ assert isinstance(value, tuple(list(six.integer_types) + [Address])) if isinstance(value, six.integer_types): # print(value, self._shift) # assert value % (self._shift + 1) == 0 return _format_integer(self, value) else: base_address = value.base_address displacement = value.displacement if isinstance(base_address, Variable): str_value = base_address.name elif isinstance(base_address, str): str_value = base_address else: raise MicroprobeCodeGenerationError( "Unable to generate the string representation of '%s'" " with value: '%s'" % (self, value)) if displacement > 0: str_value = "%s+0x%x" % (str_value, displacement) elif displacement < 0: str_value = "%s-0x%x" % (str_value, abs(displacement)) return str_value
def __mod__(self, other): """ :param other: """ if isinstance(other, self.__class__): if self.base_address != other.base_address: raise MicroprobeCodeGenerationError("I can not compute the " "module '%s' and '%s'" % (self, other)) return self.__class__(self.base_address, self.displacement + other.displacement) elif isinstance(other, six.integer_types): if isinstance(self._base_address, six.integer_types): return (self._base_address + self.displacement) % other return self.displacement % other else: raise NotImplementedError
def set_current_thread(self, idx): """ """ self._current_thread = idx if not 1 <= idx <= self._threads + 1: raise MicroprobeCodeGenerationError( "Unknown thread id: %d (min: 1, max: %d)" % (idx, self._threads + 1))
def __init__(self, name, vartype, size, align=None, value=None, address=None): """ :param name: :param vartype: :param size: :param align: (Default value = 16) :param value: (Default value = None) """ super(VariableArray, self).__init__() self._name = name self._type = vartype.lower() self._align = align self._value = value self._address = address self._elems = size if self._elems < 1: raise MicroprobeCodeGenerationError( "Array size should be greater than 0 in definition of " "variable: '%s'" % name) if align == 0: align = None if align is not None: val = math.log(align, 2) if val != int(val): raise MicroprobeCodeGenerationError( "Alignment should be power of 2 in definition of " "variable: '%s'" % name) if align is not None and address is not None: if not address.check_alignment(align): raise MicroprobeCodeGenerationError( "Alignment requirements do not match address in definition" " of variable: '%s'" % name)
def set_register(self, reg, value, context): """ :param reg: :param value: :param context: """ raise MicroprobeCodeGenerationError("Unable to set register '%s' to " " value '%d'." % (reg.name, value))
def __call__(self, building_block, target): for register_name in self._register_names: try: register = target.registers[register_name] except KeyError: raise MicroprobeCodeGenerationError( "Unknown register '%s'. Known registers: %s" % (register_name, list(target.registers.keys()))) building_block.context.add_reserved_registers([register])
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("Register '%s' already" " reserved" % str(reg)) if reg in target.control_registers and self._force_control is False: raise MicroprobeCodeGenerationError( "Register '%s' in Target definition control" " registers" % str(reg)) if callable(value): value = value() if reg.used_for_float_arithmetic: 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) 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])
def size(self): """Variable size in bytes (::class:`~.int`).""" if self._type in VAR_TYPE_LEN_DICT: return VAR_TYPE_LEN_DICT[self._type] * self._elems elif "*" in self._type: # TODO: Assuming a 64 bits address size return 8 * self._elems else: raise MicroprobeCodeGenerationError( "Unable to compute the size of type '%s' in definition " "of variable '%s'" % (self._type, self._name))
def __init__(self, target, wrapper, **kwargs): """Create a Synthesizer object. :param target: Benchmark target :type target: :class:`~.Target` :param wrapper: Wrapper object defining the output format :type wrapper: :class:`~.Wrapper` :param value: Default immediate value used for non-initialized immediates (Default: random) :type value: :class:`~.int` :param no_scratch: Disable automatic declaration of scratch variables required for code generation support (Default: False) :type no_scratch: :class:`~.bool` :param extra_raw: List of extra raw strings to be embedded in the final output :type extra_raw: :class:`~.list` of elements containing a ``name`` and a ``value`` attributes (Default: []) :return: A Synthesizer instance :rtype: :class:`~.Synthesizer` """ self._target = target # Extra arguments self._no_scratch = kwargs.get("no_scratch", False) self._raw = kwargs.get("extra_raw", {}) self._immediate = kwargs.get("value", "random") self._threads = kwargs.get("threads", 1) self._passes = {} for idx in range(1, self._threads + 1): self._passes[idx] = [] self._current_thread = 1 if isinstance(wrapper, list): if len(wrapper) != self._threads: raise MicroprobeCodeGenerationError( "Number of wrappers provided (%d) is different from " "number of threads (%d) specified in the Synthesizer" % (len(wrapper), self._threads) ) self._wrappers = wrapper else: self._wrappers = [wrapper] for dummy in range(1, self._threads): new_wrapper = copy.deepcopy(wrapper) self._wrappers.append(new_wrapper) for wrapper in self._wrappers: wrapper.set_target(target)
def __call__(self, building_block, target): """ :param building_block: :param dummy_target: """ loopvar = None for var in building_block.registered_global_vars(): if var.name == self._varname: loopvar = var break if loopvar is None: raise MicroprobeCodeGenerationError( "AddOne pass var '%s' not declared" % self._varname) context = building_block.context areg = target.get_register_for_address_arithmetic(context) context.add_reserved_registers([areg]) # Reset register to zero instrs = target.set_register(areg, 0, context) for instr in instrs: instr.add_comment("Loop counter init") building_block.add_init(instrs) instrs = target.add_to_register(areg, 1) try: instrs += target.store_integer(areg, loopvar.address, 64, context) except MicroprobeCodeGenerationError: areg = target.scratch_registers[0] instrs += target.set_register_to_address( areg, loopvar.address, context, ) context.set_register_value(areg, loopvar.address) instrs += target.store_integer(areg, loopvar.address, 64, context) for instr in instrs: instr.add_comment("Loop update") building_block.add_fini(instrs) return []
def __call__(self, building_block, target): """ :param building_block: :param target: """ replaced = False if self._every == 0: return count = 0 for bbl in building_block.cfg.bbls: for instr in bbl.instrs: if instr.architecture_type == self._instr1: count = count + 1 if (count % self._every) != 0: continue replaced = True instr.set_arch_type(self._instr2) # reserve implicit operands (if they are not already # reserved) and add them as allowed for operand_descriptor in instr.implicit_operands: register = list(operand_descriptor.type.values())[0] instr.add_allow_register(register) if operand_descriptor.is_output and register not \ in building_block.context.reserved_registers \ and register not in target.control_registers: building_block.context.add_reserved_registers( [register]) context_fix_functions = instr.check_context( building_block.context) for context_fix_function in context_fix_functions: try: context_fix_function(target, building_block) except MicroprobeUncheckableEnvironmentWarning as wue: building_block.add_warning(str(wue)) if not replaced: raise MicroprobeCodeGenerationError( "Unable to replace %s by %s every %d times" % (self._instr1.name, self._instr2.name, self._every))
def add_pass(self, synth_pass, thread_idx=None): """Add a pass to the benchmark synthesizer. :param synth_pass: New pass to add. :type synth_pass: :class:`~.Pass` """ if thread_idx is None: self._passes[self._current_thread].append(synth_pass) else: if not 1 <= thread_idx <= self._threads + 1: raise MicroprobeCodeGenerationError( "Unknown thread id: %d (min: 1, max: %d)" % (thread_idx, self._threads + 1)) self._passes[thread_idx].append(synth_pass)
def __call__(self, building_block, target): """ :param building_block: :param target: """ instructions_def = interpret_asm(self._asm, 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) if self._index < 0: building_block.add_instructions( instructions, after=building_block.cfg.bbls[-1].instrs[-1]) elif self._index == 0: building_block.add_instructions( instructions, before=building_block.cfg.bbls[0].instrs[0]) else: cindex = 0 ains = None instr = None for bbl in building_block.cfg.bbls: for instr in bbl.instrs: cindex = cindex + 1 if instr is None: raise MicroprobeCodeGenerationError( "Empty basic block found") if cindex == self._index: ains = instr building_block.add_instructions(instructions, after=ains) return building_block.add_instructions( instructions, after=building_block.cfg.bbls[-1].instrs[-1])
def set_valid_values(self, values): """ :param values: """ if len(values) == 0: raise MicroprobeCodeGenerationError( "Setting an operand without any valid value. Please check " "the definition files. Previous value: '%s'. New values: '%s'" "." % (list(self.values()), values)) for value in values: assert value in list(self.values()) self._computed_values = values self._const = len(values) == 1
def add_instructions(self, instrs, after=None, before=None): """Adds the given instruction to the building block. Adds the given instructions right after the specified instruction and before the specified one. If the condition can not be fulfilled an specification exception is raised. :param instrs: Instruction to add :type instrs: :class:`~.list` of :class:`~.Instruction` :param after: Instruction after which to add the new instruction (Default value = None) :type after: :class:`~.Instruction` :param before: Instruction before which to add the new instruction (Default value = None) :type before: :class:`~.Instruction` """ if after is None and before is None: bbl = self.cfg.last_bbl() bbl.insert_instr(instrs) elif after is not None and before is None: idx_after = self.cfg.index(after) bbl = self.cfg.get_bbl(idx_after) bbl.insert_instr(instrs, after=after) elif after is None and before is not None: idx_before = self.cfg.index(before) bbl = self.cfg.get_bbl(idx_before) bbl.insert_instr(instrs, before=before) elif after is not None and before is not None: idx_before = self.cfg.index(before) idx_after = self.cfg.index(after) if idx_before < idx_after: bbl = self.cfg.get_bbl(idx_before) bbl.insert_instr(instrs) elif idx_before == idx_after: bbl = self.cfg.get_bbl(idx_before) bbl.insert_instr(instrs, after=after, before=before) else: raise MicroprobeCodeGenerationError( "Attempt to inserst instruction in a position that it is" " not possible")
def codification(self, value): """ :param value: """ if isinstance(value, six.integer_types): return str(value >> self._shift) elif isinstance(value, Address): raise MicroprobeCodeGenerationError("Unable to codify the" " symbolic address: %s ." " Consider to add a pass to" " translate them to actual " "values " % value) else: raise NotImplementedError
def get_register_for_address_arithmetic(self, context): """ :param context: """ reg = [ reg for reg in self._address_registers if reg not in context.reserved_registers ] if len(reg) == 0: raise MicroprobeCodeGenerationError("No free registers available. " "Change your policy.") # REG0 tends to have special meaning and it is usually at the # beginning, move it reg = reg[1:] + [reg[0]] return reg[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)
def reset_instruction(self, instr, new_instr): """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
def __rsub__(self, other): """ :param other: """ if isinstance(other, (Address, InstructionAddress)): if self.base_address != other.base_address: raise MicroprobeCodeGenerationError("I can not sub '%s' " "and '%s'" % (self, other)) return other.displacement - self.displacement elif isinstance(other, six.integer_types): return self.__class__(self.base_address, self.displacement - other) else: raise NotImplementedError( "Substraction not implemented for %s and %s " "objects" % (self.__class__, other.__class__))
def __sub__(self, other): """ :param other: """ if isinstance(other, self.__class__): if self.base_address != other.base_address: raise MicroprobeCodeGenerationError("I can not sub '%s' " "and '%s'" % (self, other)) return self.displacement - other.displacement elif isinstance(other, six.integer_types): return self.__class__(self.base_address, self.displacement - other) else: LOG.critical("%s != %s", self, other) LOG.critical("%s != %s", type(self), type(other)) raise NotImplementedError
def __rsub__(self, other): """ :param other: """ if isinstance(other, (Address, InstructionAddress)): if self.base_address != other.base_address: raise MicroprobeCodeGenerationError( "I can not sub '%s' " "and '%s'" % (self, other) ) return other.displacement - self.displacement elif isinstance(other, int): return self.__class__(self.base_address, self.displacement - other) else: print(self.__class__, other.__class__) raise NotImplementedError
def _wrap_thread(self, bench, thread_id): """Wrap a thread in benchmark. This function wraps a thread using the synthesizer wrapper. The wrapping process is the process of converting the internal representation of the benchmark to the actual string that is written to a file, adding the necessary prologue and epilogue bytes of data. :param bench: Benchmark to wrap. :type bench: :class:`~.Benchmark` :param thread_id: Thread to wrap :type thread_id : :class:`~.int` :return: A string representation of the benchmark :rtype: :class:`~.str` """ bench.set_current_thread(thread_id) self.set_current_thread(thread_id) thread_str = [] thread_str.append(self.wrapper.start_main()) if 'CODE_HEADER' in self._raw: thread_str.append("\n" + self._raw['CODE_HEADER'] + "\n") for var in bench.registered_global_vars(): if var.value is None: thread_str.append( self.wrapper.init_global_var( var, self._immediate ) ) thread_str.append(self.wrapper.post_var()) # TODO: This is hardcoded and assumes a loop always. Needs to be more # generic: pass a building block to a wrapper and it automatically # returns the required string for instr in bench.init: thread_str.append(self.wrapper.wrap_ins(instr)) code_str = [] first = True instr = None for bbl in bench.cfg.bbls: for instr in bbl.instrs: if first is True: first = False if bench.init: code_str.append( self.wrapper.start_loop( instr, bench.init[0] ) ) else: code_str.append(self.wrapper.start_loop(instr, instr)) code_str.append(self.wrapper.wrap_ins(instr)) if instr is None: raise MicroprobeCodeGenerationError( "No instructions found in benchmark" ) thread_str.extend(code_str) for instr in bench.fini: thread_str.append(self.wrapper.wrap_ins(instr)) last_instr = instr thread_str.append(self.wrapper.end_loop(last_instr)) if 'CODE_FOOTER' in self._raw: thread_str.append("\n" + self._raw['CODE_FOOTER'] + "\n") thread_str.append(self.wrapper.end_main()) return thread_str
def set_register_to_address(self, register, address, context, force_absolute=False, force_relative=False): instrs = [] assert address is not None LOG.debug("Begin setting '%s' to address '%s'", register, address) if isinstance(address.base_address, Variable): LOG.debug("Base address is a Variable: %s", address.base_address) closest = context.get_closest_address_value(address) if context.register_has_value(address): present_reg = context.registers_get_value(address)[0] displacement = 0 LOG.debug("Address already in register '%s'", present_reg) elif closest is not None: present_reg, taddress = closest displacement = address.displacement - taddress.displacement LOG.debug("Closest value '%s' found in '%s'", taddress, present_reg) LOG.debug("Displacement needed: %s", displacement) elif context.register_has_value( Address(base_address=address.base_address)): present_reg = context.registers_get_value( Address(base_address=address.base_address))[0] displacement = address.displacement LOG.debug("Base address '%s' found in '%s'", taddress, present_reg) LOG.debug("Displacement needed: %s", displacement) else: present_reg = None displacement = None LOG.debug("Present_reg: %s", present_reg) LOG.debug("Displacement: %s", displacement) if present_reg is not None: if displacement != 0 and abs(displacement) < (2**15): addi_ins = self.new_instruction("ADDI_V0") addi_ins.set_operands( [register, present_reg, displacement]) instrs.append(addi_ins) LOG.debug("Computing directly from context (short)") return instrs if present_reg != register: or_ins = self.new_instruction("OR_V0") or_ins.set_operands([present_reg, register, present_reg]) instrs.append(or_ins) if displacement != 0: instrs += self.add_to_register(register, displacement) LOG.debug("Computing directly from context (long)") return instrs if context.symbolic and not force_absolute and not force_relative: # TODO: This should be a call to the environment object because # the implementation depends on the environment # Base address can be an instruction label (str) or # a Variable instance basename = address.base_address if not isinstance(address.base_address, str): basename = address.base_address.name lis_ins = self.new_instruction("ADDIS_V1") lis_ins.operands()[0].set_value(register) lis_ins.operands()[1].set_value(0) lis_ins.operands()[2].set_value("%s@highest" % basename, check=False) instrs.append(lis_ins) ori_ins = self.new_instruction("ORI_V0") ori_ins.operands()[0].set_value(register) ori_ins.operands()[1].set_value(register) ori_ins.operands()[2].set_value("%s@higher" % basename, check=False) instrs.append(ori_ins) rldicr_ins = self.new_instruction("RLDICR_V0") rldicr_ins.set_operands([register, register, 32, 31]) instrs.append(rldicr_ins) oris_ins = self.new_instruction("ORIS_V0") oris_ins.operands()[0].set_value(register) oris_ins.operands()[1].set_value(register) oris_ins.operands()[2].set_value("%s@h" % basename, check=False) instrs.append(oris_ins) ori_ins = self.new_instruction("ORI_V0") ori_ins.operands()[0].set_value(register) ori_ins.operands()[1].set_value(register) ori_ins.operands()[2].set_value("%s@l" % basename, check=False) instrs.append(ori_ins) if address.displacement != 0: instrs += self.add_to_register(register, address.displacement) LOG.debug("End Loading symbolic reference") return instrs LOG.debug("Context not symbolic") base_address = address.base_address displacement = address.displacement LOG.debug("Base_address: %s", base_address) LOG.debug("Displacement: %s", displacement) if isinstance(base_address, Variable): LOG.debug("Get absolute address") displacement += base_address.address.displacement base_address = base_address.address.base_address LOG.debug("Base_address 2: %s", base_address) LOG.debug("Displacement 2: %s", displacement) if isinstance(base_address, str): if base_address == "data": base_address = Address(base_address=base_address) elif base_address == "code": base_address = InstructionAddress(base_address=base_address) source_register = None if context.register_has_value(base_address): source_register = context.registers_get_value(base_address)[0] else: for reg, value in context.register_values.items(): if not isinstance(value, Address): continue if (Address(base_address=value.base_address) == base_address.base_address): source_register = reg displacement += base_address.displacement base_address = Address( base_address=base_address.base_address) break if value.base_address == base_address.base_address: source_register = reg displacement += base_address.displacement base_address = Address( base_address=base_address.base_address) break if source_register is None or displacement >= (2**31): # Not source register found if base_address.base_address == "data": value = context.data_segment elif base_address.base_address == "code": value = context.code_segment else: LOG.debug(context.dump()) raise MicroprobeCodeGenerationError( "Unable to generate " "the base address: '%s'" " for target address: '%s'." % (base_address, address)) if abs(displacement) >= (2**31): value = value + displacement displacement = 0 assert (value is not None) instrs += self.set_register(register, value, context) if source_register is not None and source_register != register: or_ins = self.new_instruction("OR_V0") or_ins.set_operands([source_register, register, source_register]) instrs.append(or_ins) if displacement != 0: instrs += self.add_to_register(register, displacement) LOG.debug("End address generation") return instrs