def _apply_found_ISA_data(self, isa_data, operands): """ Create operand dictionary containing src/dst operands out of the ISA data entry and the oeprands of an instruction form If breaks_pedendency_on_equal_operands is True (configuted per instruction in ISA db) and all operands are equal, place operand into destination only. :param dict isa_data: ISA DB entry :param list operands: operands of the instruction form :returns: `dict` -- operands dictionary with src/dst assignment """ op_dict = {} op_dict["source"] = [] op_dict["destination"] = [] op_dict["src_dst"] = [] # handle dependency breaking instructions if "breaks_pedendency_on_equal_operands" in isa_data and operands[ 1:] == operands[:-1]: op_dict["destination"] += operands if "hidden_operands" in isa_data: op_dict["destination"] += [ AttrDict.convert_dict({ hop["class"]: { k: hop[k] for k in ["class", "source", "destination"] } }) for hop in isa_data["hidden_operands"] ] return op_dict for i, op in enumerate(isa_data["operands"]): if op["source"] and op["destination"]: op_dict["src_dst"].append(operands[i]) continue if op["source"]: op_dict["source"].append(operands[i]) continue if op["destination"]: op_dict["destination"].append(operands[i]) continue # check for hidden operands like flags or registers if "hidden_operands" in isa_data: # add operand(s) to semantic_operands of instruction form for op in isa_data["hidden_operands"]: dict_key = ("src_dst" if op["source"] and op["destination"] else "source" if op["source"] else "destination") hidden_op = {op["class"]: {}} key_filter = ["class", "source", "destination"] for key in [k for k in op.keys() if k not in key_filter]: hidden_op[op["class"]][key] = op[key] hidden_op = AttrDict.convert_dict(hidden_op) op_dict[dict_key].append(hidden_op) return op_dict
def parse_instruction(self, instruction): """ Parse instruction in asm line. :param str instruction: Assembly line string. :returns: `dict` -- parsed instruction form """ result = self.instruction_parser.parseString(instruction, parseAll=True).asDict() result = AttrDict.convert_dict(result) operands = [] # Add operands to list # Check first operand if 'operand1' in result: operands.append(self.process_operand(result['operand1'])) # Check second operand if 'operand2' in result: operands.append(self.process_operand(result['operand2'])) # Check third operand if 'operand3' in result: operands.append(self.process_operand(result['operand3'])) # Check fourth operand if 'operand4' in result: operands.append(self.process_operand(result['operand4'])) return_dict = AttrDict({ self.INSTRUCTION_ID: result['mnemonic'], self.OPERANDS_ID: operands, self.COMMENT_ID: ' '.join(result[self.COMMENT_ID]) if self.COMMENT_ID in result else None, }) return return_dict
def process_register_list(self, register_list): """Post-process register lists (e.g., {r0,r3,r5}) and register ranges (e.g., {r0-r7})""" # Remove unnecessarily created dictionary entries during parsing rlist = [] dict_name = '' if 'list' in register_list: dict_name = 'list' if 'range' in register_list: dict_name = 'range' for r in register_list[dict_name]: rlist.append( AttrDict.convert_dict( self.list_element.parseString(r, parseAll=True).asDict())) index = register_list.get('index', None) new_dict = AttrDict({dict_name: rlist, 'index': index}) if len(new_dict[dict_name]) == 1: return AttrDict({self.REGISTER_ID: new_dict[dict_name][0]}) return AttrDict({self.REGISTER_ID: new_dict})
def parse_instruction(self, instruction): """ Parse instruction in asm line. :param str instruction: Assembly line string. :returns: `dict` -- parsed instruction form """ result = self.instruction_parser.parseString(instruction, parseAll=True).asDict() result = AttrDict.convert_dict(result) operands = [] # Add operands to list # Check first operand if "operand1" in result: operand = self.process_operand(result["operand1"]) operands.extend(operand) if isinstance(operand, list) else operands.append(operand) # Check second operand if "operand2" in result: operand = self.process_operand(result["operand2"]) operands.extend(operand) if isinstance(operand, list) else operands.append(operand) # Check third operand if "operand3" in result: operand = self.process_operand(result["operand3"]) operands.extend(operand) if isinstance(operand, list) else operands.append(operand) # Check fourth operand if "operand4" in result: operand = self.process_operand(result["operand4"]) operands.extend(operand) if isinstance(operand, list) else operands.append(operand) # Check fifth operand if "operand5" in result: operand = self.process_operand(result["operand5"]) operands.extend(operand) if isinstance(operand, list) else operands.append(operand) return_dict = AttrDict( { self.INSTRUCTION_ID: result.mnemonic, self.OPERANDS_ID: operands, self.COMMENT_ID: " ".join(result[self.COMMENT_ID]) if self.COMMENT_ID in result else None, } ) return return_dict
def _apply_found_ISA_data(self, isa_data, operands): """ Create operand dictionary containing src/dst operands out of the ISA data entry and the oeprands of an instruction form :param dict isa_data: ISA DB entry :param list operands: operands of the instruction form :returns: `dict` -- operands dictionary with src/dst assignment """ op_dict = {} op_dict['source'] = [] op_dict['destination'] = [] op_dict['src_dst'] = [] for i, op in enumerate(isa_data['operands']): if op['source'] and op['destination']: op_dict['src_dst'].append(operands[i]) continue if op['source']: op_dict['source'].append(operands[i]) continue if op['destination']: op_dict['destination'].append(operands[i]) continue # check for hidden operands like flags or registers if 'hidden_operands' in isa_data: # add operand(s) to semantic_operands of instruction form for op in isa_data['hidden_operands']: dict_key = ( 'src_dst' if op['source'] and op['destination'] else 'source' if op['source'] else 'destination' ) hidden_op = {op['class']: {}} key_filter = ['class', 'source', 'destination'] for key in [k for k in op.keys() if k not in key_filter]: hidden_op[op['class']][key] = op[key] hidden_op = AttrDict.convert_dict(hidden_op) op_dict[dict_key].append(hidden_op) return op_dict
def parse_line(self, line, line_number=None): """ Parse line and return instruction form. :param str line: line of assembly code :param line_number: default None, identifier of instruction form :type line_number: int, optional :return: ``dict`` -- parsed asm line (comment, label, directive or instruction form) """ instruction_form = AttrDict({ self.INSTRUCTION_ID: None, self.OPERANDS_ID: [], self.DIRECTIVE_ID: None, self.COMMENT_ID: None, self.LABEL_ID: None, 'line': line, 'line_number': line_number, }) result = None # 1. Parse comment try: result = self.process_operand( self.comment.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.COMMENT_ID] = ' '.join( result[self.COMMENT_ID]) except pp.ParseException: pass # 2. Parse label if result is None: try: result = self.process_operand( self.label.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.LABEL_ID] = result[self.LABEL_ID]['name'] if self.COMMENT_ID in result[self.LABEL_ID]: instruction_form[self.COMMENT_ID] = ' '.join( result[self.LABEL_ID][self.COMMENT_ID]) except pp.ParseException: pass # 3. Parse directive if result is None: try: result = self.process_operand( self.directive.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.DIRECTIVE_ID] = AttrDict({ 'name': result[self.DIRECTIVE_ID]['name'], 'parameters': result[self.DIRECTIVE_ID]['parameters'], }) if self.COMMENT_ID in result[self.DIRECTIVE_ID]: instruction_form[self.COMMENT_ID] = ' '.join( result[self.DIRECTIVE_ID][self.COMMENT_ID]) except pp.ParseException: pass # 4. Parse instruction if result is None: try: result = self.parse_instruction(line) except pp.ParseException: raise ValueError( 'Could not parse instruction on line {}: {!r}'.format( line_number, line)) instruction_form[self.INSTRUCTION_ID] = result[self.INSTRUCTION_ID] instruction_form[self.OPERANDS_ID] = result[self.OPERANDS_ID] instruction_form[self.COMMENT_ID] = result[self.COMMENT_ID] return instruction_form
def parse_line(self, line, line_number=None): """ Parse line and return instruction form. :param str line: line of assembly code :param line_number: identifier of instruction form, defautls to None :type line_number: int, optional :return: `dict` -- parsed asm line (comment, label, directive or instruction form) """ instruction_form = AttrDict({ self.INSTRUCTION_ID: None, self.OPERANDS_ID: [], self.DIRECTIVE_ID: None, self.COMMENT_ID: None, self.LABEL_ID: None, 'line': line, 'line_number': line_number, }) result = None # 1. Parse comment try: result = self.process_operand( self.comment.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.COMMENT_ID] = ' '.join( result[self.COMMENT_ID]) except pp.ParseException: pass # 1.2 check for llvm-mca marker try: result = self.process_operand( self.llvm_markers.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.COMMENT_ID] = ' '.join( result[self.COMMENT_ID]) except pp.ParseException: pass # 2. Parse label if result is None: try: result = self.process_operand( self.label.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.LABEL_ID] = result[self.LABEL_ID].name if self.COMMENT_ID in result[self.LABEL_ID]: instruction_form[self.COMMENT_ID] = ' '.join( result[self.LABEL_ID][self.COMMENT_ID]) except pp.ParseException: pass # 3. Parse directive if result is None: try: result = self.process_operand( self.directive.parseString(line, parseAll=True).asDict()) result = AttrDict.convert_dict(result) instruction_form[self.DIRECTIVE_ID] = AttrDict({ 'name': result[self.DIRECTIVE_ID].name, 'parameters': result[self.DIRECTIVE_ID].parameters, }) if self.COMMENT_ID in result[self.DIRECTIVE_ID]: instruction_form[self.COMMENT_ID] = ' '.join( result[self.DIRECTIVE_ID][self.COMMENT_ID]) except pp.ParseException: pass # 4. Parse instruction if result is None: try: result = self.parse_instruction(line) except (pp.ParseException, KeyError): print( '\n\n*-*-*-*-*-*-*-*-*-*-\n{}: {}\n*-*-*-*-*-*-*-*-*-*-\n\n' .format(line_number, line)) instruction_form[self.INSTRUCTION_ID] = result[self.INSTRUCTION_ID] instruction_form[self.OPERANDS_ID] = result[self.OPERANDS_ID] instruction_form[self.COMMENT_ID] = result[self.COMMENT_ID] return instruction_form
def _get_directive(self, parser, directive): return AttrDict.convert_dict( parser.process_operand( parser.directive.parseString( directive, parseAll=True).asDict())).directive
def _get_label(self, parser, label): return AttrDict.convert_dict( parser.process_operand( parser.label.parseString(label, parseAll=True).asDict())).label
def _get_comment(self, parser, comment): return ' '.join( AttrDict.convert_dict( parser.process_operand( parser.comment.parseString( comment, parseAll=True).asDict())).comment)
def assign_src_dst(self, instruction_form): """Update instruction form dictionary with source, destination and flag information.""" # if the instruction form doesn't have operands or is None, there's nothing to do if instruction_form['operands'] is None or instruction_form['instruction'] is None: instruction_form['semantic_operands'] = AttrDict( {'source': [], 'destination': [], 'src_dst': []} ) return # check if instruction form is in ISA yaml, otherwise apply standard operand assignment # (one dest, others source) isa_data = self._isa_model.get_instruction( instruction_form['instruction'], instruction_form['operands'] ) if ( isa_data is None and self._isa == 'x86' and instruction_form['instruction'][-1] in self.GAS_SUFFIXES ): # Check for instruction without GAS suffix isa_data = self._isa_model.get_instruction( instruction_form['instruction'][:-1], instruction_form['operands'] ) operands = instruction_form['operands'] op_dict = {} assign_default = False if isa_data: # load src/dst structure from isa_data op_dict = self._apply_found_ISA_data(isa_data, operands) else: # Couldn't found instruction form in ISA DB assign_default = True # check for equivalent register-operands DB entry if LD/ST if any(['memory' in op for op in operands]): operands_reg = self.substitute_mem_address(instruction_form['operands']) isa_data_reg = self._isa_model.get_instruction( instruction_form['instruction'], operands_reg ) if ( isa_data_reg is None and self._isa == 'x86' and instruction_form['instruction'][-1] in self.GAS_SUFFIXES ): # Check for instruction without GAS suffix isa_data_reg = self._isa_model.get_instruction( instruction_form['instruction'][:-1], operands_reg ) if isa_data_reg: assign_default = False op_dict = self._apply_found_ISA_data(isa_data_reg, operands) if assign_default: # no irregular operand structure, apply default op_dict['source'] = self._get_regular_source_operands(instruction_form) op_dict['destination'] = self._get_regular_destination_operands(instruction_form) op_dict['src_dst'] = [] # post-process pre- and post-indexing for aarch64 memory operands if self._isa == 'aarch64': for operand in [op for op in op_dict['source'] if 'memory' in op]: if ('post_indexed' in operand['memory'] and operand['memory']['post_indexed']) or ( 'pre_indexed' in operand['memory'] and operand['memory']['pre_indexed'] ): op_dict['src_dst'].append(AttrDict.convert_dict( {'register': operand['memory']['base']})) for operand in [op for op in op_dict['destination'] if 'memory' in op]: if ('post_indexed' in operand['memory'] and operand['memory']['post_indexed']) or ( 'pre_indexed' in operand['memory'] and operand['memory']['pre_indexed'] ): op_dict['src_dst'].append(AttrDict.convert_dict( {'register': operand['memory']['base']})) # store operand list in dict and reassign operand key/value pair instruction_form['semantic_operands'] = AttrDict.convert_dict(op_dict) # assign LD/ST flags instruction_form['flags'] = ( instruction_form['flags'] if 'flags' in instruction_form else [] ) if self._has_load(instruction_form): instruction_form['flags'] += [INSTR_FLAGS.HAS_LD] if self._has_store(instruction_form): instruction_form['flags'] += [INSTR_FLAGS.HAS_ST]
def assign_src_dst(self, instruction_form): """Update instruction form dictionary with source, destination and flag information.""" # if the instruction form doesn't have operands or is None, there's nothing to do if instruction_form["operands"] is None or instruction_form[ "instruction"] is None: instruction_form["semantic_operands"] = AttrDict({ "source": [], "destination": [], "src_dst": [] }) return # check if instruction form is in ISA yaml, otherwise apply standard operand assignment # (one dest, others source) isa_data = self._isa_model.get_instruction( instruction_form["instruction"], instruction_form["operands"]) if (isa_data is None and self._isa == "x86" and instruction_form["instruction"][-1] in self.GAS_SUFFIXES): # Check for instruction without GAS suffix isa_data = self._isa_model.get_instruction( instruction_form["instruction"][:-1], instruction_form["operands"]) operands = instruction_form["operands"] op_dict = {} assign_default = False if isa_data: # load src/dst structure from isa_data op_dict = self._apply_found_ISA_data(isa_data, operands) else: # Couldn't found instruction form in ISA DB assign_default = True # check for equivalent register-operands DB entry if LD/ST if any(["memory" in op for op in operands]): operands_reg = self.substitute_mem_address( instruction_form["operands"]) isa_data_reg = self._isa_model.get_instruction( instruction_form["instruction"], operands_reg) if (isa_data_reg is None and self._isa == "x86" and instruction_form["instruction"][-1] in self.GAS_SUFFIXES): # Check for instruction without GAS suffix isa_data_reg = self._isa_model.get_instruction( instruction_form["instruction"][:-1], operands_reg) if isa_data_reg: assign_default = False op_dict = self._apply_found_ISA_data( isa_data_reg, operands) if assign_default: # no irregular operand structure, apply default op_dict["source"] = self._get_regular_source_operands( instruction_form) op_dict["destination"] = self._get_regular_destination_operands( instruction_form) op_dict["src_dst"] = [] # post-process pre- and post-indexing for aarch64 memory operands if self._isa == "aarch64": for operand in [op for op in op_dict["source"] if "memory" in op]: post_indexed = ("post_indexed" in operand["memory"] and operand["memory"]["post_indexed"]) pre_indexed = ("pre_indexed" in operand["memory"] and operand["memory"]["pre_indexed"]) if post_indexed or pre_indexed: op_dict["src_dst"].append( AttrDict.convert_dict({ "register": operand["memory"]["base"], "pre_indexed": pre_indexed, "post_indexed": post_indexed })) for operand in [ op for op in op_dict["destination"] if "memory" in op ]: post_indexed = ("post_indexed" in operand["memory"] and operand["memory"]["post_indexed"]) pre_indexed = ("pre_indexed" in operand["memory"] and operand["memory"]["pre_indexed"]) if post_indexed or pre_indexed: op_dict["src_dst"].append( AttrDict.convert_dict({ "register": operand["memory"]["base"], "pre_indexed": pre_indexed, "post_indexed": post_indexed })) # store operand list in dict and reassign operand key/value pair instruction_form["semantic_operands"] = AttrDict.convert_dict(op_dict) # assign LD/ST flags instruction_form["flags"] = instruction_form[ "flags"] if "flags" in instruction_form else [] if self._has_load(instruction_form): instruction_form["flags"] += [INSTR_FLAGS.HAS_LD] if self._has_store(instruction_form): instruction_form["flags"] += [INSTR_FLAGS.HAS_ST]
def check_for_loopcarried_dep(self, kernel): """ Try to find loop-carried dependencies in given kernel. :param kernel: Parsed asm kernel with assigned semantic information :type kernel: list :returns: `dict` -- dependency dictionary with all cyclic LCDs """ multiplier = len(kernel) + 1 # increase line number for second kernel loop kernel_length = len(kernel) first_line_no = kernel[0].line_number kernel_copy = [AttrDict.convert_dict(d) for d in copy.deepcopy(kernel)] tmp_kernel = kernel + kernel_copy for i, instruction_form in enumerate(tmp_kernel[kernel_length:]): tmp_kernel[ i + kernel_length].line_number = instruction_form.line_number * multiplier # get dependency graph dg = self.create_DG(tmp_kernel) # build cyclic loop-carried dependencies loopcarried_deps = [ (node, list( nx.algorithms.simple_paths.all_simple_paths( dg, node, node * multiplier))) for node in dg.nodes if node < first_line_no * multiplier and node == int(node) ] # filter others and create graph loopcarried_deps = list( chain.from_iterable([ list(product([dep_chain[0]], dep_chain[1])) for dep_chain in loopcarried_deps ])) # adjust line numbers, filter duplicates # and add reference to kernel again loopcarried_deps_dict = {} tmp_list = [] for i, dep in enumerate(loopcarried_deps): nodes = [ int(n / multiplier) for n in dep[1] if n >= first_line_no * multiplier ] loopcarried_deps[i] = (dep[0], nodes) for dep in loopcarried_deps: is_subset = False for other_dep in [x for x in loopcarried_deps if x[0] != dep[0]]: if set(dep[1]).issubset(set( other_dep[1])) and dep[0] in other_dep[1]: is_subset = True if not is_subset: tmp_list.append(dep) loopcarried_deps = tmp_list for dep in loopcarried_deps: nodes = [] for n in dep[1]: self._get_node_by_lineno(int(n))['latency_lcd'] = 0 for n in dep[1]: node = self._get_node_by_lineno(int(n)) if int(n) != n and int(n) in dep[1]: node['latency_lcd'] += node['latency'] - node[ 'latency_wo_load'] else: node['latency_lcd'] += node['latency_wo_load'] nodes.append(node) loopcarried_deps_dict[dep[0]] = { 'root': self._get_node_by_lineno(dep[0]), 'dependencies': nodes, } return loopcarried_deps_dict