From 615ef82f0407f6da1ff6da0f06367c608578b66c Mon Sep 17 00:00:00 2001 From: stefandesouza Date: Mon, 28 Aug 2023 15:19:46 +0200 Subject: [PATCH] Changes to accomodate the new OO style --- osaca/db_interface.py | 0 osaca/frontend.py | 0 osaca/osaca.py | 5 +- osaca/parser/instruction_form.py | 36 ++++++--- osaca/parser/parser_x86att.py | 10 +-- osaca/semantics/arch_semantics.py | 9 +-- osaca/semantics/hw_model.py | 79 +++++++++++--------- osaca/semantics/isa_semantics.py | 118 ++++++++++-------------------- osaca/semantics/kernel_dg.py | 118 ++++++++++++++++-------------- osaca/semantics/marker_utils.py | 7 +- tests/test_semantics.py | 32 ++++---- 11 files changed, 198 insertions(+), 216 deletions(-) mode change 100755 => 100644 osaca/db_interface.py mode change 100755 => 100644 osaca/frontend.py mode change 100755 => 100644 osaca/osaca.py diff --git a/osaca/db_interface.py b/osaca/db_interface.py old mode 100755 new mode 100644 diff --git a/osaca/frontend.py b/osaca/frontend.py old mode 100755 new mode 100644 diff --git a/osaca/osaca.py b/osaca/osaca.py old mode 100755 new mode 100644 index 51d46b6..55b70eb --- a/osaca/osaca.py +++ b/osaca/osaca.py @@ -430,10 +430,7 @@ def get_unmatched_instruction_ratio(kernel): """Return ratio of unmatched from total instructions in kernel.""" unmatched_counter = 0 for instruction in kernel: - if ( - INSTR_FLAGS.TP_UNKWN in instruction["flags"] - and INSTR_FLAGS.LT_UNKWN in instruction["flags"] - ): + if INSTR_FLAGS.TP_UNKWN in instruction.flags and INSTR_FLAGS.LT_UNKWN in instruction.flags: unmatched_counter += 1 return unmatched_counter / len(kernel) diff --git a/osaca/parser/instruction_form.py b/osaca/parser/instruction_form.py index f6dc22e..9ae1392 100644 --- a/osaca/parser/instruction_form.py +++ b/osaca/parser/instruction_form.py @@ -4,18 +4,6 @@ from osaca.parser.directive import DirectiveOperand class InstructionForm: - # Identifiers for operand types - COMMENT_ID = "comment" - DIRECTIVE_ID = "directive" - IMMEDIATE_ID = "immediate" - LABEL_ID = "label" - IDENTIFIER_ID = "identifier" - MEMORY_ID = "memory" - REGISTER_ID = "register" - SEGMENT_EXT_ID = "segment_extension" - INSTRUCTION_ID = "instruction" - OPERANDS_ID = "operands" - def __init__( self, INSTRUCTION_ID=None, @@ -91,6 +79,18 @@ class InstructionForm: def flags(self): return self._FLAGS + @property + def throughput(self): + return self._THROUGHPUT + + @property + def latency(self): + return self._LATENCY + + @property + def latency_wo_load(self): + return self._LATENCY_WO_LOAD + @semantic_operands.setter def semantic_operands(self, semantic_operands): self._SEMANTIC_OPERANDS = semantic_operands @@ -135,6 +135,18 @@ class InstructionForm: def flags(self, flags): self._FLAGS = flags + @throughput.setter + def throughput(self, throughput): + self._THROUGHPUT = throughput + + @latency.setter + def latency(self, latency): + self._LATENCY = latency + + @latency_wo_load.setter + def latency_wo_load(self, latency_wo_load): + self._LATENCY_WO_LOAD = latency_wo_load + def __repr__(self): return f"InstructionForm(INSTRUCTION_ID={self._INSTRUCTION_ID}, OPERANDS_ID={self._OPERANDS_ID}, DIRECTIVE_ID={self._DIRECTIVE_ID}, COMMENT_ID={self._COMMENT_ID}, LABEL_ID={self._LABEL_ID}, LINE={self._LINE}, LINE_NUMBER={self._LINE_NUMBER}, SEMANTIC_OPERANDS={self._SEMANTIC_OPERANDS})" diff --git a/osaca/parser/parser_x86att.py b/osaca/parser/parser_x86att.py index e763e14..e1447ff 100644 --- a/osaca/parser/parser_x86att.py +++ b/osaca/parser/parser_x86att.py @@ -386,8 +386,8 @@ class ParserX86ATT(BaseParser): def is_reg_dependend_of(self, reg_a, reg_b): """Check if ``reg_a`` is dependent on ``reg_b``""" # Normalize name - reg_a_name = reg_a["name"].upper() - reg_b_name = reg_b["name"].upper() + reg_a_name = reg_a.name.upper() + reg_b_name = reg_b.name.upper() # Check if they are the same registers if reg_a_name == reg_b_name: @@ -428,8 +428,8 @@ class ParserX86ATT(BaseParser): def is_basic_gpr(self, register): """Check if register is a basic general purpose register (ebi, rax, ...)""" - if any(char.isdigit() for char in register["name"]) or any( - register["name"].lower().startswith(x) for x in ["mm", "xmm", "ymm", "zmm"] + if any(char.isdigit() for char in register.name) or any( + register.name.lower().startswith(x) for x in ["mm", "xmm", "ymm", "zmm"] ): return False return True @@ -446,7 +446,7 @@ class ParserX86ATT(BaseParser): """Check if register is a vector register""" if register is None: return False - if register["name"].rstrip(string.digits).lower() in [ + if register.name.rstrip(string.digits).lower() in [ "mm", "xmm", "ymm", diff --git a/osaca/semantics/arch_semantics.py b/osaca/semantics/arch_semantics.py index 1aacf36..0706eff 100644 --- a/osaca/semantics/arch_semantics.py +++ b/osaca/semantics/arch_semantics.py @@ -69,9 +69,7 @@ class ArchSemantics(ISASemantics): indices = [port_list.index(p) for p in ports] # check if port sum of used ports for uop are unbalanced port_sums = self._to_list(itemgetter(*indices)(self.get_throughput_sum(kernel))) - instr_ports = self._to_list( - itemgetter(*indices)(instruction_form.port_pressure) - ) + instr_ports = self._to_list(itemgetter(*indices)(instruction_form.port_pressure)) if len(set(port_sums)) > 1: # balance ports # init list for keeping track of the current change @@ -110,9 +108,7 @@ class ArchSemantics(ISASemantics): ][0] instruction_form.port_pressure[zero_index] = 0.0 # Remove from further balancing - indices = [ - p for p in indices if instruction_form.port_pressure[p] > 0 - ] + indices = [p for p in indices if instruction_form.port_pressure[p] > 0] instr_ports = self._to_list( itemgetter(*indices)(instruction_form.port_pressure) ) @@ -193,6 +189,7 @@ class ArchSemantics(ISASemantics): instruction_data = self._machine_model.get_instruction( instruction_form.instruction, instruction_form.operands ) + if ( not instruction_data and self._isa == "x86" diff --git a/osaca/semantics/hw_model.py b/osaca/semantics/hw_model.py index aa23f21..a01bcbd 100644 --- a/osaca/semantics/hw_model.py +++ b/osaca/semantics/hw_model.py @@ -14,6 +14,11 @@ import ruamel.yaml from osaca import __version__, utils from osaca.parser import ParserX86ATT from ruamel.yaml.compat import StringIO +from osaca.parser.operand import Operand +from osaca.parser.memory import MemoryOperand +from osaca.parser.register import RegisterOperand +from osaca.parser.immediate import ImmediateOperand +from osaca.parser.identifier import IdentifierOperand class MachineModel(object): @@ -21,7 +26,7 @@ class MachineModel(object): INTERNAL_VERSION = 1 # increase whenever self._data format changes to invalidate cache! _runtime_cache = {} - def __init__(self, arch=None, path_to_yaml=None, isa=None, lazy=False): + def __init__(self, arch=None, path_to_yaml=None, isa=None, lazy=True): if not arch and not path_to_yaml: if not isa: raise ValueError("One of arch, path_to_yaml and isa must be specified") @@ -97,6 +102,7 @@ class MachineModel(object): # Normalize instruction_form names (to UPPERCASE) and build dict for faster access: self._data["instruction_forms_dict"] = defaultdict(list) for iform in self._data["instruction_forms"]: + print(iform) iform["name"] = iform["name"].upper() self._data["instruction_forms_dict"][iform["name"]].append(iform) self._data["internal_version"] = self.INTERNAL_VERSION @@ -128,6 +134,7 @@ class MachineModel(object): if name is None: return None name_matched_iforms = self._data["instruction_forms_dict"].get(name.upper(), []) + # print(name_matched_iforms) try: return next( instruction_form @@ -264,7 +271,7 @@ class MachineModel(object): def get_full_instruction_name(instruction_form): """Get one instruction name string including the mnemonic and all operands.""" operands = [] - for op in instruction_form["operands"]: + for op in instruction_form.operands: op_attrs = [ y + ":" + str(op[y]) for y in list(filter(lambda x: True if x != "class" else False, op)) @@ -526,7 +533,9 @@ class MachineModel(object): def _check_operands(self, i_operand, operand): """Check if the types of operand ``i_operand`` and ``operand`` match.""" # check for wildcard - if self.WILDCARD in operand.name: + if (isinstance(operand, Operand) and operand.name == self.WILDCARD) or ( + not isinstance(operand, Operand) and self.WILDCARD in operand + ): if ( "class" in i_operand and i_operand["class"] == "register" @@ -601,25 +610,27 @@ class MachineModel(object): def _check_x86_operands(self, i_operand, operand): """Check if the types of operand ``i_operand`` and ``operand`` match.""" - if "class" in operand.name: - # compare two DB entries - return self._compare_db_entries(i_operand, operand) + # if "class" in operand.name: + # compare two DB entries + # return self._compare_db_entries(i_operand, operand) # register - if "register" in operand.name: + if isinstance(operand, RegisterOperand): if i_operand["class"] != "register": return False return self._is_x86_reg_type(i_operand, operand, consider_masking=False) # memory - if "memory" in operand.name: + if isinstance(operand, MemoryOperand): if i_operand["class"] != "memory": return False return self._is_x86_mem_type(i_operand, operand) # immediate - if "immediate" in operand.name or operand.value != None: + if isinstance(operand, ImmediateOperand): + # if "immediate" in operand.name or operand.value != None: return i_operand["class"] == "immediate" and i_operand["imd"] == "int" # identifier (e.g., labels) - if "identifier" in operand.name: + if isinstance(operand, IdentifierOperand): return i_operand["class"] == "identifier" + return self._compare_db_entries(i_operand, operand) def _compare_db_entries(self, operand_1, operand_2): """Check if operand types in DB format (i.e., not parsed) match.""" @@ -676,29 +687,29 @@ class MachineModel(object): return True return False # check for wildcards - if i_reg_name == self.WILDCARD or reg["name"] == self.WILDCARD: + if i_reg_name == self.WILDCARD or reg.name == self.WILDCARD: return True # differentiate between vector registers (mm, xmm, ymm, zmm) and others (gpr) parser_x86 = ParserX86ATT() if parser_x86.is_vector_register(reg): - if reg["name"].rstrip(string.digits).lower() == i_reg_name: + if reg.name.rstrip(string.digits).lower() == i_reg_name: # Consider masking and zeroing for AVX512 if consider_masking: mask_ok = zero_ok = True - if "mask" in reg or "mask" in i_reg: + if reg.mask != None or "mask" in i_reg: # one instruction is missing the masking while the other has it mask_ok = False # check for wildcard if ( ( - "mask" in reg - and reg["mask"].rstrip(string.digits).lower() == i_reg.get("mask") + reg.mask != None + and reg.mask.rstrip(string.digits).lower() == i_reg.get("mask") ) - or reg.get("mask") == self.WILDCARD + or reg.mask == self.WILDCARD or i_reg.get("mask") == self.WILDCARD ): mask_ok = True - if bool("zeroing" in reg) ^ bool("zeroing" in i_reg): + if bool(reg.zeroing) ^ bool("zeroing" in i_reg): # one instruction is missing zeroing while the other has it zero_ok = False # check for wildcard @@ -776,48 +787,48 @@ class MachineModel(object): if ( # check base ( - (mem["base"] is None and i_mem["base"] is None) + (mem.base is None and i_mem["base"] is None) or i_mem["base"] == self.WILDCARD - or self._is_x86_reg_type(i_mem["base"], mem["base"]) + or self._is_x86_reg_type(i_mem["base"], mem.base) ) # check offset and ( - mem["offset"] == i_mem["offset"] + mem.offset == i_mem["offset"] or i_mem["offset"] == self.WILDCARD or ( - mem["offset"] is not None - and "identifier" in mem["offset"] + mem.offset is not None + and "identifier" in mem.offset and i_mem["offset"] == "identifier" ) or ( - mem["offset"] is not None - and "value" in mem["offset"] + mem.offset is not None + and "value" in mem.offset and ( - i_mem["offset"] == "imd" - or (i_mem["offset"] is None and mem["offset"]["value"] == "0") + i_mem.offset == "imd" + or (i_mem["offset"] is None and mem.offset["value"] == "0") ) ) or ( - mem["offset"] is not None - and "identifier" in mem["offset"] + mem.offset is not None + and "identifier" in mem.offset and i_mem["offset"] == "id" ) ) # check index and ( - mem["index"] == i_mem["index"] + mem.index == i_mem["index"] or i_mem["index"] == self.WILDCARD or ( - mem["index"] is not None - and "name" in mem["index"] - and self._is_x86_reg_type(i_mem["index"], mem["index"]) + mem.index is not None + and "name" in mem.index + and self._is_x86_reg_type(i_mem["index"], mem.index) ) ) # check scale and ( - mem["scale"] == i_mem["scale"] + mem.scale == i_mem["scale"] or i_mem["scale"] == self.WILDCARD - or (mem["scale"] != 1 and i_mem["scale"] != 1) + or (mem.scale != 1 and i_mem["scale"] != 1) ) ): return True diff --git a/osaca/semantics/isa_semantics.py b/osaca/semantics/isa_semantics.py index 3eb2159..a90c0fa 100644 --- a/osaca/semantics/isa_semantics.py +++ b/osaca/semantics/isa_semantics.py @@ -4,39 +4,10 @@ from itertools import chain from osaca import utils from osaca.parser import AttrDict, ParserAArch64, ParserX86ATT from osaca.parser.memory import MemoryOperand +from osaca.parser.register import RegisterOperand from .hw_model import MachineModel -class SemanticForm: - def __init__(self, SOURCE_ID = [], DESTINATION_ID = [], SRC_DST = []): - self._SOURCE_ID = SOURCE_ID - self._DESTINATION_ID = DESTINATION_ID - self._SRC_DST = SRC_DST - - @property - def source(self): - return self._SOURCE_ID - - @source.setter - def source(self, source): - self._SOURCE_ID = source - - @property - def destination(self): - return self._DESTINATION_ID - - @destination.setter - def destination(self, destination): - self._DESTINATION_ID = destination - - @property - def src_dst(self): - return self._SRC_DST - - @src_dst.setter - def src_dst(self, src_dst): - self._SRC_DST = src_dst - class INSTR_FLAGS: """ @@ -77,9 +48,7 @@ class ISASemantics(object): """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 = SemanticForm( - SOURCE_ID = [], DESTINATION_ID = [], SRC_DST = [] - ) + instruction_form.semantic_operands = {"source": [], "destination": [], "src_dst": []} return # check if instruction form is in ISA yaml, otherwise apply standard operand assignment # (one dest, others source) @@ -111,8 +80,7 @@ class ISASemantics(object): # Couldn't found instruction form in ISA DB assign_default = True # check for equivalent register-operands DB entry if LD/ST - - if isinstance(operands, MemoryOperand) and operands.name == "memory": + if any([isinstance(op, MemoryOperand) 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 @@ -146,46 +114,34 @@ class ISASemantics(object): 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"] - ) + for operand in [op for op in op_dict["source"] if isinstance(op, MemoryOperand)]: + post_indexed = operand.post_indexed + pre_indexed = operand.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, - } - ) + { + "register": operand.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"] - ) + for operand in [op for op in op_dict["destination"] if isinstance(op, MemoryOperand)]: + post_indexed = operand.post_indexed + pre_indexed = operand.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, - } - ) + { + "register": operand.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) + instruction_form.semantic_operands = op_dict # assign LD/ST flags - instruction_form.flags = ( - instruction_form.flags if instruction_form.flags != [] else [] - ) + # 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): @@ -198,15 +154,15 @@ class ISASemantics(object): Empty dict if no changes of registers occured. None for registers with unknown changes. If only_postindexed is True, only considers changes due to post_indexed memory references. """ - if instruction_form.get("instruction") is None: + if instruction_form.instruction is None: return {} dest_reg_names = [ - op.register.get("prefix", "") + op.register.name + op.prefix if op.prefix != None else "" + op.name for op in chain( - instruction_form.semantic_operands.destination, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["destination"], + instruction_form.semantic_operands["src_dst"], ) - if "register" in op + if isinstance(op, RegisterOperand) ] isa_data = self._isa_model.get_instruction( instruction_form.instruction, instruction_form.operands @@ -243,7 +199,7 @@ class ISASemantics(object): operand_state = {} # e.g., {'op1': {'name': 'rax', 'value': 0}} 0 means unchanged for o in instruction_form.operands: - if "pre_indexed" in o.get("memory", {}): + if isinstance(o, MemoryOperand) and o.pre_indexed: # Assuming no isa_data.operation if isa_data is not None and isa_data.get("operation", None) is not None: raise ValueError( @@ -340,20 +296,20 @@ class ISASemantics(object): def _has_load(self, instruction_form): """Check if instruction form performs a LOAD""" for operand in chain( - instruction_form.semantic_operands.source, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["source"], + instruction_form.semantic_operands["src_dst"], ): - if operand.name == "memory": + if isinstance(operand, MemoryOperand): return True return False def _has_store(self, instruction_form): """Check if instruction form perfroms a STORE""" for operand in chain( - instruction_form.semantic_operands.destination, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["destination"], + instruction_form.semantic_operands["src_dst"], ): - if "memory" in operand: + if isinstance(operand, MemoryOperand): return True return False @@ -386,7 +342,9 @@ class ISASemantics(object): def substitute_mem_address(self, operands): """Create memory wildcard for all memory operands""" - return [self._create_reg_wildcard() if "memory" in op else op for op in operands] + return [ + self._create_reg_wildcard() if isinstance(op, MemoryOperand) else op for op in operands + ] def _create_reg_wildcard(self): """Wildcard constructor""" diff --git a/osaca/semantics/kernel_dg.py b/osaca/semantics/kernel_dg.py index b36c4de..8ba9894 100644 --- a/osaca/semantics/kernel_dg.py +++ b/osaca/semantics/kernel_dg.py @@ -9,6 +9,8 @@ from multiprocessing import Manager, Process, cpu_count import networkx as nx from osaca.semantics import INSTR_FLAGS, ArchSemantics, MachineModel +from osaca.parser.memory import MemoryOperand +from osaca.parser.register import RegisterOperand class KernelDG(nx.DiGraph): @@ -60,8 +62,7 @@ class KernelDG(nx.DiGraph): dg = nx.DiGraph() for i, instruction_form in enumerate(kernel): dg.add_node(instruction_form.line_number) - print(dg.nodes[instruction_form.line_number]) - dg.nodes[instruction_form.line_number].instruction_form = instruction_form + dg.nodes[instruction_form.line_number]["instruction_form"] = instruction_form # add load as separate node if existent if ( INSTR_FLAGS.HAS_LD in instruction_form.flags @@ -271,8 +272,8 @@ class KernelDG(nx.DiGraph): if instruction_form.semantic_operands is None: return for dst in chain( - instruction_form.semantic_operands.destination, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["destination"], + instruction_form.semantic_operands["src_dst"], ): # TODO instructions before must be considered as well, if they update registers # not used by insruction_form. E.g., validation/build/A64FX/gcc/O1/gs-2d-5pt.marked.s @@ -281,27 +282,32 @@ class KernelDG(nx.DiGraph): for i, instr_form in enumerate(instructions): self._update_reg_changes(instr_form, register_changes) # print(" TO", instr_form.line, register_changes) - if "register" in dst: + if isinstance(dst, RegisterOperand): # read of register - if self.is_read(dst.register, instr_form): + if self.is_read(dst, instr_form): if dst.get("pre_indexed", False) or dst.get("post_indexed", False): yield instr_form, ["p_indexed"] else: yield instr_form, [] # write to register -> abort - if self.is_written(dst.register, instr_form): + if self.is_written(dst, instr_form): break - if "flag" in dst and flag_dependencies: + if ( + not isinstance(dst, RegisterOperand) + and not isinstance(dst, MemoryOperandOperand) + and "flag" in dst + and flag_dependencies + ): # read of flag if self.is_read(dst.flag, instr_form): yield instr_form, [] # write to flag -> abort if self.is_written(dst.flag, instr_form): break - if "memory" in dst: + if isinstance(dst, MemoryOperand): # base register is altered during memory access - if "pre_indexed" in dst.memory: - if self.is_written(dst.memory.base, instr_form): + if dist.pre_indexed != None: + if self.is_written(dst.base, instr_form): break # if dst.memory.base: # if self.is_read(dst.memory.base, instr_form): @@ -309,18 +315,18 @@ class KernelDG(nx.DiGraph): # if dst.memory.index: # if self.is_read(dst.memory.index, instr_form): # yield instr_form, [] - if "post_indexed" in dst.memory: + if dst.post_indexed: # Check for read of base register until overwrite - if self.is_written(dst.memory.base, instr_form): + if self.is_written(dst.base, instr_form): break # TODO record register changes # (e.g., mov, leaadd, sub, inc, dec) in instructions[:i] # and pass to is_memload and is_memstore to consider relevance. # load from same location (presumed) - if self.is_memload(dst.memory, instr_form, register_changes): + if self.is_memload(dst, instr_form, register_changes): yield instr_form, ["storeload_dep"] # store to same location (presumed) - if self.is_memstore(dst.memory, instr_form, register_changes): + if self.is_memstore(dst, instr_form, register_changes): break self._update_reg_changes(instr_form, register_changes, only_postindexed=True) @@ -364,32 +370,32 @@ class KernelDG(nx.DiGraph): if instruction_form.semantic_operands is None: return is_read for src in chain( - instruction_form.semantic_operands.source, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["source"], + instruction_form.semantic_operands["src_dst"], ): - if "register" in src: - is_read = self.parser.is_reg_dependend_of(register, src.register) or is_read - if "flag" in src: + if isinstance(src, RegisterOperand): + is_read = self.parser.is_reg_dependend_of(register, src) or is_read + if ( + not isinstance(src, RegisterOperand) + and not isinstance(src, MemoryOperand) + and "flag" in src + ): is_read = self.parser.is_flag_dependend_of(register, src.flag) or is_read - if "memory" in src: - if src.memory.base is not None: - is_read = self.parser.is_reg_dependend_of(register, src.memory.base) or is_read - if src.memory.index is not None: - is_read = ( - self.parser.is_reg_dependend_of(register, src.memory.index) or is_read - ) + if isinstance(src, MemoryOperand): + if src.base is not None: + is_read = self.parser.is_reg_dependend_of(register, src.base) or is_read + if src.index is not None: + is_read = self.parser.is_reg_dependend_of(register, src.index) or is_read # Check also if read in destination memory address for dst in chain( - instruction_form.semantic_operands.destination, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["destination"], + instruction_form.semantic_operands["src_dst"], ): - if "memory" in dst: - if dst.memory.base is not None: - is_read = self.parser.is_reg_dependend_of(register, dst.memory.base) or is_read - if dst.memory.index is not None: - is_read = ( - self.parser.is_reg_dependend_of(register, dst.memory.index) or is_read - ) + if isinstance(dst, MemoryOperand): + if dst.base is not None: + is_read = self.parser.is_reg_dependend_of(register, dst.base) or is_read + if dst.index is not None: + is_read = self.parser.is_reg_dependend_of(register, dst.index) or is_read return is_read def is_memload(self, mem, instruction_form, register_changes={}): @@ -455,28 +461,28 @@ class KernelDG(nx.DiGraph): if instruction_form.semantic_operands is None: return is_written for dst in chain( - instruction_form.semantic_operands.destination, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["destination"], + instruction_form.semantic_operands["src_dst"], ): - if "register" in dst: - is_written = self.parser.is_reg_dependend_of(register, dst.register) or is_written - if "flag" in dst: + if isinstance(dst, RegisterOperand): + is_written = self.parser.is_reg_dependend_of(register, dst) or is_written + if ( + not isinstance(dst, RegisterOperand) + and not isinstance(dst, MemoryOperand) + and "flag" in dst + ): is_written = self.parser.is_flag_dependend_of(register, dst.flag) or is_written - if "memory" in dst: - if "pre_indexed" in dst.memory or "post_indexed" in dst.memory: - is_written = ( - self.parser.is_reg_dependend_of(register, dst.memory.base) or is_written - ) + if isinstance(dst, MemoryOperand): + if dst.pre_indexed or dst.post_indexed: + is_written = self.parser.is_reg_dependend_of(register, dst.base) or is_written # Check also for possible pre- or post-indexing in memory addresses for src in chain( - instruction_form.semantic_operands.source, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["source"], + instruction_form.semantic_operands["src_dst"], ): - if "memory" in src: - if "pre_indexed" in src.memory or "post_indexed" in src.memory: - is_written = ( - self.parser.is_reg_dependend_of(register, src.memory.base) or is_written - ) + if isinstance(src, MemoryOperand): + if src.pre_indexed or src.post_indexed: + is_written = self.parser.is_reg_dependend_of(register, src.base) or is_written return is_written def is_memstore(self, mem, instruction_form, register_changes={}): @@ -485,10 +491,10 @@ class KernelDG(nx.DiGraph): if instruction_form.semantic_operands is None: return is_store for dst in chain( - instruction_form.semantic_operands.destination, - instruction_form.semantic_operands.src_dst, + instruction_form.semantic_operands["destination"], + instruction_form.semantic_operands["src_dst"], ): - if "memory" in dst: + if isinstance(dst, MemoryOperand): is_store = mem == dst["memory"] or is_store return is_store diff --git a/osaca/semantics/marker_utils.py b/osaca/semantics/marker_utils.py index ad3b54d..d8d1772 100644 --- a/osaca/semantics/marker_utils.py +++ b/osaca/semantics/marker_utils.py @@ -150,7 +150,7 @@ def find_marked_section( "immediate" in source and parser.normalize_imd(source.immediate) == mov_vals[0] and "register" in destination - and parser.get_full_reg_name(destination['register']) == mov_reg + and parser.get_full_reg_name(destination["register"]) == mov_reg ): # operands of first instruction match start, check for second one match, line_count = match_bytes(lines, i + 1, nop_bytes) @@ -161,7 +161,7 @@ def find_marked_section( "immediate" in source and parser.normalize_imd(source.immediate) == mov_vals[1] and "register" in destination - and parser.get_full_reg_name(destination['register']) == mov_reg + and parser.get_full_reg_name(destination["register"]) == mov_reg ): # operand of first instruction match end, check for second one match, line_count = match_bytes(lines, i + 1, nop_bytes) @@ -169,7 +169,8 @@ def find_marked_section( # return line of the marker index_end = i except TypeError: - print(i, line) + pass + # print("TESTER",i, line) if index_start != -1 and index_end != -1: break return index_start, index_end diff --git a/tests/test_semantics.py b/tests/test_semantics.py index 1d22750..af2d764 100755 --- a/tests/test_semantics.py +++ b/tests/test_semantics.py @@ -256,38 +256,38 @@ class TestSemanticTools(unittest.TestCase): def test_src_dst_assignment_x86(self): for instruction_form in self.kernel_x86: with self.subTest(instruction_form=instruction_form): - if instruction_form["semantic_operands"] is not None: - self.assertTrue("source" in instruction_form["semantic_operands"]) - self.assertTrue("destination" in instruction_form["semantic_operands"]) - self.assertTrue("src_dst" in instruction_form["semantic_operands"]) + if instruction_form.semantic_operands is not None: + self.assertTrue("source" in instruction_form.semantic_operands) + self.assertTrue("destination" in instruction_form.semantic_operands) + self.assertTrue("src_dst" in instruction_form.semantic_operands) def test_src_dst_assignment_AArch64(self): for instruction_form in self.kernel_AArch64: with self.subTest(instruction_form=instruction_form): - if instruction_form["semantic_operands"] is not None: - self.assertTrue("source" in instruction_form["semantic_operands"]) - self.assertTrue("destination" in instruction_form["semantic_operands"]) - self.assertTrue("src_dst" in instruction_form["semantic_operands"]) + if instruction_form.semantic_operands is not None: + self.assertTrue("source" in instruction_form.semantic_operands) + self.assertTrue("destination" in instruction_form.semantic_operands) + self.assertTrue("src_dst" in instruction_form.semantic_operands) def test_tp_lt_assignment_x86(self): self.assertTrue("ports" in self.machine_model_csx) port_num = len(self.machine_model_csx["ports"]) for instruction_form in self.kernel_x86: with self.subTest(instruction_form=instruction_form): - self.assertTrue("throughput" in instruction_form) - self.assertTrue("latency" in instruction_form) - self.assertIsInstance(instruction_form["port_pressure"], list) - self.assertEqual(len(instruction_form["port_pressure"]), port_num) + self.assertTrue(instruction_form.throughput != None) + self.assertTrue(instruction_form.latency != None) + self.assertIsInstance(instruction_form.port_pressure, list) + self.assertEqual(len(instruction_form.port_pressure), port_num) def test_tp_lt_assignment_AArch64(self): self.assertTrue("ports" in self.machine_model_tx2) port_num = len(self.machine_model_tx2["ports"]) for instruction_form in self.kernel_AArch64: with self.subTest(instruction_form=instruction_form): - self.assertTrue("throughput" in instruction_form) - self.assertTrue("latency" in instruction_form) - self.assertIsInstance(instruction_form["port_pressure"], list) - self.assertEqual(len(instruction_form["port_pressure"]), port_num) + self.assertTrue(instruction_form.throughput != None) + self.assertTrue(instruction_form.latency != None) + self.assertIsInstance(instruction_form.port_pressure, list) + self.assertEqual(len(instruction_form.port_pressure), port_num) def test_optimal_throughput_assignment(self): # x86