def __init__(self, name, descr, regs, address_base, address_index, floating_point, vector): """ :param name: :param descr: :param regs: :param address_base: :param address_index: :param floating_point: :param vector: """ super(OperandReg, self).__init__(name, descr) if isinstance(regs, list): self._regs = OrderedDict() for reg in regs: self._regs[reg] = [reg] else: self._regs = regs self._ab = address_base self._ai = address_index self._fp = floating_point self._vector = vector if self._fp is None: self._fp = list(set([reg.type for reg in self._regs ]))[0].used_for_float_arithmetic if self._vector is None: self._vector = list(set([reg.type for reg in self._regs ]))[0].used_for_vector_arithmetic
def __init__(self): """ """ super(Benchmark, self).__init__() self._cfg = microprobe.code.cfg.Cfg() self._global_vars = OrderedDict() self._init = [] self._fini = [] self._vardisplacement = 0 self._context = None
def reset_dictionaries(): """ Reset the local dependency tracking dictionaries """ allregs = list(target.registers.values()) allregs.sort(key=lambda x: int(re.findall(r'\d+', str(x))[0])) for reg in allregs: if reg.type not in lastreaded: lastreaded[reg.type] = OrderedDict() if reg.type not in lastdefined: lastdefined[reg.type] = OrderedDict() if reg.type not in lastdefread: lastdefread[reg.type] = OrderedDict() lastreaded[reg.type][reg] = 0 if reg not in building_block.context.reserved_registers: lastdefined[reg.type][reg] = 0 lastdefread[reg.type][reg] = 0
class OperandReg(Operand): """Class to represent a register operand. """ def __init__(self, name, descr, regs, address_base, address_index, floating_point, vector): """ :param name: :param descr: :param regs: :param address_base: :param address_index: :param floating_point: :param vector: """ super(OperandReg, self).__init__(name, descr) if isinstance(regs, list): self._regs = OrderedDict() for reg in regs: self._regs[reg] = [reg] else: self._regs = regs self._ab = address_base self._ai = address_index self._fp = floating_point self._vector = vector if self._fp is None: self._fp = list(set([reg.type for reg in self._regs ]))[0].used_for_float_arithmetic if self._vector is None: self._vector = list(set([reg.type for reg in self._regs ]))[0].used_for_vector_arithmetic def values(self): """Return the possible value of the operand. :rtype: :class:`list` of :class:`~.Register` """ return list(self._regs.keys()) def representation(self, value): """ :param value: """ return value.representation def codification(self, value): """ :param value: """ return value.codification def random_value(self): """Return a random possible value for the operand. :rtype: :class:`~.Register` """ return list(self._regs.keys())[random.randrange(0, len(self._regs))] def access(self, value): """ :param value: """ return self._regs[value] def __contains__(self, value): """ :param value: """ if not isinstance(value, Register): return False return value.name in [reg.name for reg in self.values()] def copy(self): """ """ return OperandReg(self.name, self.description, self._regs.copy(), self._ab, self._ai, self._fp, self._vector) def set_valid_values(self, values): """ :param values: """ assert len(values) > 0 for value in self.values(): if value not in values: del self._regs[value] assert sorted(self.values()) == sorted(values), \ "\nValues: %s \nValues(): %s" % (sorted(values), sorted(self.values())) self._const = len(values) == 1
def import_definition(filenames, registers): """ :param filenames: :param registers: """ LOG.debug("Start") operands = {} operands_duplicated = {} register_types = tuple([reg.type.name for reg in registers.values()]) for filename in filenames: ope_data = read_yaml(filename, SCHEMA) if ope_data is None: continue for elem in ope_data: name = elem["Name"] descr = elem.get("Description", "No description") override = elem.get("Override", False) key = [] try: if "Registers" in elem: regnames = elem["Registers"] if isinstance(regnames, list): if len(regnames) == 1 and \ regnames[0] in register_types: regs = [ reg for reg in registers.values() if reg.type.name == regnames[0] ] else: regs = [ registers[regname] for regname in natural_sort(regnames) ] key.append(tuple(regnames)) else: regs = OrderedDict() for regname in natural_sort(regnames): regs[registers[regname]] = [] for regname2 in regnames[regname]: regs[registers[regname]].append( registers[regname2]) key.append( tuple([(k, tuple(v)) for k, v in regnames.items()])) address_base = elem.get("AddressBase", False) address_index = elem.get("AddressIndex", False) floating_point = elem.get("FloatingPoint", None) vector = elem.get("Vector", None) key.append(address_base) key.append(address_index) key.append(floating_point) key.append(vector) # Filter out Register without # representation (N/A) # # These are pseudo registers used in # simulation/emulation environment. # They are not architected registers. regs = [reg for reg in regs if reg.representation != 'N/A'] operand = OperandReg(name, descr, regs, address_base, address_index, floating_point, vector) elif "Min" in elem and "Max" in elem: minval = elem["Min"] maxval = elem["Max"] step = elem.get("Step", 1) novalues = elem.get("Except", []) address_index = elem.get("AddressIndex", False) shift = elem.get("Shift", 0) add = elem.get("Add", 0) key.append(minval) key.append(maxval) key.append(step) key.append(tuple(novalues)) key.append(address_index) key.append(shift) key.append(add) operand = OperandImmRange(name, descr, minval, maxval, step, address_index, shift, novalues, add) elif "Values" in elem: values = tuple(elem["Values"]) key.append(tuple(values)) operand = OperandValueSet(name, descr, values) elif "Value" in elem: value = elem["Value"] key.append(value) operand = OperandConst(name, descr, value) elif "Register" in elem: reg = registers[elem["Register"]] address_base = elem.get("AddressBase", False) address_index = elem.get("AddressIndex", False) floating_point = elem.get("FloatingPoint", False) vector = elem.get("Vector", False) key.append(elem["Register"]) key.append(address_base) key.append(address_index) key.append(floating_point) key.append(vector) operand = OperandConstReg(name, descr, reg, address_base, address_index, floating_point, vector) elif "Relative" in elem: mindispl = elem["MinDisplacement"] maxdispl = elem["MaxDisplacement"] relative = elem["Relative"] shift = elem.get("Shift", 0) except_ranges = elem.get("ExceptRange", []) key.append(mindispl) key.append(maxdispl) key.append(shift) key.append(tuple([tuple(elem) for elem in except_ranges])) operand = InstructionAddressRelativeOperand( name, descr, maxdispl, mindispl, shift, except_ranges, relative) else: raise MicroprobeArchitectureDefinitionError( "Operand definition '%s' in '%s' not supported" % (name, filename)) tkey = tuple(key) if tkey in operands_duplicated: LOG.warning( "Similar definition of operands: '%s' and" " '%s'. Check if definition needed.", name, operands_duplicated[tkey]) else: operands_duplicated[tkey] = name except KeyError as exception: raise MicroprobeArchitectureDefinitionError( "Definition" " of operand '%s' " "uses an unknown " "register in '%s'" "\nMissing defini" "tion of: %s" % (name, filename, exception)) if name in operands and not override: raise MicroprobeArchitectureDefinitionError( "Duplicated definition of operand '%s' found in '%s'" % (name, filename)) LOG.debug(operand) operands[name] = operand LOG.debug("End") return operands
class Benchmark(BuildingBlock): """Class to represent a benchmark (highest level building block).""" def __init__(self): """ """ super(Benchmark, self).__init__() self._cfg = microprobe.code.cfg.Cfg() self._global_vars = OrderedDict() self._init = [] self._fini = [] self._vardisplacement = 0 self._context = None @property def init(self): """Initialization instructions Initialization instructions (:class:`~.list` of :class:`~.Instruction`) """ return self._init def add_init(self, inits): """Appends the specified list of instructions to initialization list :param inits: List of instructions to be added :type inits: :class:`~.list` of :class:`~.Instruction` """ LOG.debug("Add Init") for init in inits: LOG.debug("INIT: %s", init) self._init = self._init + inits def rm_init(self, inits): """Removes from the initialization list the specified instructions. :param inits: List of instructions to be removed :type inits: :class:`~.list` of :class:`~.Instruction` """ self._init = self._init[0:-len(inits)] def add_fini(self, finis): """Appends the specified list of instructions to the finalization list :param finis: List of instructions to be added :type finis: :class:`~.list` of :class:`~.Instruction` """ self._fini = self._fini + finis @property def fini(self): """Finalization instructions (:class:`~.list` of :class:`~.Instruction`)""" return self._fini @property def cfg(self): """Returns the benchmark control flow graph.""" return self._cfg def set_cfg(self, cfg): """Sets the benchmarks control flow graph. :param cfg: Control flow graph :type cfg: :class:`~.Cfg` """ self._cfg = cfg def registered_global_vars(self): """Returns the list of registered global variables.""" return list(self._global_vars.values()) def register_var(self, var, context): """Registers the given variable as a global variable. :param var: Variable to register :type var: :class:`~.Variable` """ LOG.debug("Registering global var: '%s'", var.name) if var.name in self._global_vars: var2 = self._global_vars[var.name] if (var.value == var2.value and var.address == var2.address and var.address is not None and MICROPROBE_RC['safe_bin']): LOG.warning("Variable: '%s' registered multiple times!", var.name) return LOG.critical("Registered variables: %s", list(self._global_vars.keys())) raise MicroprobeCodeGenerationError( "Variable already registered: %s" % (var.name)) self._global_vars[var.name] = var if context.symbolic: LOG.debug("Context symbolic. No need to track base addresses") var_address = Address(base_address=var.name, displacement=0) var.set_address(var_address) elif var.address is None: LOG.debug("Context not symbolic and variable address not set") # if (self._vardisplacement == 0 and # context.data_segment is not None): # self._vardisplacement = context.data_segment address_ok = False while not address_ok: var_address = Address(base_address="data", displacement=self._vardisplacement) LOG.debug("Address before alignment: %s", var_address) align = var.align if align is None: align = 1 LOG.debug("Variable alignment: %s", align) LOG.debug("Current address: %s", var_address) if var_address.displacement % align != 0: # alignment needed var_address += align - (var_address.displacement % align) LOG.debug("Address after alignment: %s", var_address) LOG.debug("Current var displacement: %x", self._vardisplacement) var.set_address(var_address) over = self._check_variable_overlap(var) if over is not None: self._vardisplacement = max( self._vardisplacement, over.address.displacement + over.size) continue address_ok = True LOG.debug("Variable registered at address: '%s'", var_address) self._vardisplacement = var_address.displacement + var.size else: LOG.debug("Using pre-defined address: '%s'", var.address) over = self._check_variable_overlap(var) if over is not None: raise MicroprobeCodeGenerationError( "Variable '%s' overlaps with variable '%s'" % (var.name, over.name)) def _check_variable_overlap(self, var): vara = var.address vara2 = vara + var.size for rvar in self._global_vars.values(): if var.name == rvar.name: continue rvara = rvar.address rvara2 = rvara + rvar.size if rvara < vara2 and rvara2 >= vara2: print(1) return rvar if rvara <= vara and rvara2 > vara: print(2) return rvar if rvara >= vara and rvara2 <= vara2: print(3) return rvar if rvara <= vara and rvara2 >= vara2: print(4) return rvar return None def set_context(self, context): """Set the execution context of the building block. :param context: Execution context :type context: :class:`~.Context` """ self._context = context @property def context(self): """Return benchmark's context""" return self._context 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") @property def code_size(self): """Return benchmark's size""" size = 0 for bbl in self.cfg.bbls: for instr in bbl.instrs: size += instr.architecture_type.format.length return size @property def labels(self): """Return the a list of the current defined labels and symbols""" labels = [key.upper() for key in self._global_vars.keys()] for bbl in self.cfg.bbls: for instr in bbl.instrs: if instr.label is not None: labels.append(instr.label.upper()) labels += [ instr.label.upper() for instr in self._fini if instr.label is not None ] labels += [ instr.label.upper() for instr in self._init if instr.label is not None ] return labels
def __call__(self, building_block, target): """ :param building_block: :param target: """ allregs = target.registers lastdefined = {} lastused = {} rregs = set(building_block.context.reserved_registers) # TODO: All this pass has to be reimplemented for reg in allregs.values(): if reg in rregs: # TODO: rregs can be used but only as input, now we discard # them for input and output continue if reg.type not in lastused: lastused[reg.type] = OrderedDict() lastdefined[reg.type] = OrderedDict() lastdefined[reg.type][reg] = 0 lastused[reg.type][reg] = 1 idx = 1 for bbl in building_block.cfg.bbls: for instr in bbl.instrs: dependency_ok = False used = [] defined = [] distance = self._dd() for operand in instr.operands(): if operand.value is not None: # operand already set pass elif operand.type.immediate: if self._immediate != "random": try: svalue = self._immediate while callable(svalue): svalue = svalue() operand.set_value(svalue) except MicroprobeValueError: LOG.warning( "Operand '%s' in instruction '%s' " "not modeled properly. Tried to " "'%s' value ...", operand.type, instr.name, svalue ) value = list(operand.type.values())[0] operand.set_value(value) LOG.warning("Operand set to: '%s'", value) else: operand.set_value(operand.type.random_value()) elif operand.type.address_relative: LOG.warning( "Operand '%s' in instruction '%s' " "not modeled properly", operand.type, instr.name ) operand.set_value(list(operand.type.values())[0]) else: if operand.is_input and distance > 0 and \ not dependency_ok and idx > distance: LOG.debug("Setting dependency distance") regs = list(operand.type.values()) valid_values = [] for reg in regs: if len( rregs.intersection( operand.type.access(reg) ) ) == 0: valid_values.append(reg) if (len(valid_values) == 0 and operand.is_input and not operand.is_output): # Try to use reserved values # if the operand is reading only valid_values = list(operand.type.values()) LOG.debug("Current idx: %d", idx) LOG.debug("Requested idx: %d", idx - distance) reg = microprobe.utils.distrib.sort_by_distance( valid_values, lastdefined[regs[0].type], lastused[regs[0].type], idx - distance, instr, idx ) LOG.debug("%s selected", reg) if reg in lastdefined[regs[0].type]: LOG.debug( "Last defined: %s", lastdefined[ regs[0].type ][reg] ) if reg in lastused[regs[0].type]: LOG.debug( "Last used: %s", lastused[ regs[0].type ][reg] ) dependency_ok = True else: regs = list(operand.type.values()) valid_values = [] for reg in regs: if len( rregs.intersection( operand.type.access(reg) ) ) == 0: valid_values.append(reg) if (len(valid_values) == 0 and operand.is_input and not operand.is_output): # Try to use reserved values # if the operand is reading only valid_values = list(operand.type.values()) if len(valid_values) == 0: LOG.critical("Instruction: %s", instr) LOG.critical("Operand: %s", operand) LOG.critical("Possible all values: %s", sorted(operand.type.values())) LOG.critical( "Possible values: %s", sorted(regs)) LOG.critical( "Reserved values: %s", sorted(rregs)) raise MicroprobeCodeGenerationError( "Unable to find proper operand" " values for register allocation." ) reg = microprobe.utils.distrib.sort_by_usage( valid_values, lastused[regs[0].type], lastdefined[regs[0].type] ) operand.set_value(reg) if self._min is False: for reg in operand.uses(): LOG.debug("Updating usage of %s", reg) LOG.debug(list(lastused[reg.type].keys())) if reg in lastused[reg.type]: del lastused[reg.type][reg] lastused[reg.type][reg] = idx used.append(reg) LOG.debug(list(lastused[reg.type].keys())) if operand.is_output: for reg in operand.sets(): LOG.debug("Updating definition of %s", reg) if reg in lastdefined[reg.type]: del lastdefined[reg.type][reg] lastdefined[reg.type][reg] = idx defined.append(reg) LOG.debug("Updating usage of %s", reg) LOG.debug(list(lastused[reg.type].keys())) if reg in lastused[reg.type]: del lastused[reg.type][reg] lastused[reg.type][reg] = idx used.append(reg) LOG.debug(list(lastused[reg.type].keys())) # if self._min is True: # for reg in instr.uses(): # if reg in lastused[reg.type]: # del lastused[reg.type][reg] # lastused[reg.type][reg] = idx # used.append(reg) # for reg in instr.sets(): # if reg in lastused[reg.type]: # del lastused[reg.type][reg] # lastused[reg.type][reg] = idx # used.append(reg) fail = rregs.intersection(set(instr.sets())) if len(fail) > 0 and not self._relax: for reg in fail: if not instr.allows(reg): raise MicroprobeCodeGenerationError( "Instruction '%s' sets a reserved" " register but not allowed before:" "Register: %s. Reserved: %s" % ( instr.name, reg.name, sorted([reg.name for reg in rregs]) ) ) for reg in instr.uses(): if reg not in used and reg not in rregs: LOG.debug("Updating usage of %s", reg) lastused[reg.type][reg] = idx for reg in instr.sets(): if reg not in defined and reg not in rregs: LOG.debug("Updating definition of %s", reg) del lastdefined[reg.type][reg] lastdefined[reg.type][reg] = idx LOG.debug("Instruction: %s", instr) LOG.debug("Last used rank:") for rtype in lastused: for reg in lastused[rtype]: if lastused[rtype][ reg ] > 0 and reg.name.startswith("GPR"): LOG.debug("%s: %d", reg.name, lastused[rtype][reg]) LOG.debug("Last defined rank:") for rtype in lastdefined: for reg in lastdefined[rtype]: if lastdefined[rtype][ reg ] > 0 and reg.name.startswith("GPR"): LOG.debug( "%s: %d", reg.name, lastdefined[rtype][reg] ) idx = idx + 1