diff --git a/.github/workflows/test-n-publish.yml b/.github/workflows/test-n-publish.yml index 77783e6..808852b 100644 --- a/.github/workflows/test-n-publish.yml +++ b/.github/workflows/test-n-publish.yml @@ -22,7 +22,8 @@ jobs: python -m pip install bs4 sudo apt-get -y install graphviz libgraphviz-dev pkg-config python -m pip install pygraphviz - python -m pip install kerncraft + #python -m pip install kerncraft + python -m pip install git+https://github.com/RRZE-HPC/kerncraft.git@InstrucForm python -m pip install -e . - name: Test run: | diff --git a/osaca/db_interface.py b/osaca/db_interface.py index e7794d5..419a7d4 100644 --- a/osaca/db_interface.py +++ b/osaca/db_interface.py @@ -13,6 +13,7 @@ from osaca.parser.memory import MemoryOperand from osaca.parser.register import RegisterOperand from osaca.parser.immediate import ImmediateOperand +from osaca.parser.instruction_form import instructionForm def sanity_check(arch: str, verbose=False, internet_check=False, output_file=sys.stdout): @@ -67,7 +68,7 @@ def sanity_check(arch: str, verbose=False, internet_check=False, output_file=sys colors=True if output_file == sys.stdout else False, ) print(report, file=output_file) - + return not any([missing_port_pressure, bad_operand]) @@ -147,14 +148,9 @@ def _get_asmbench_output(input_data, isa): mnemonic = i_form.split("-")[0] operands = i_form.split("-")[1].split("_") operands = [_create_db_operand(op, isa) for op in operands] - entry = { - "name": mnemonic, - "operands": operands, - "throughput": _validate_measurement(float(input_data[i + 2].split()[1]), "tp"), - "latency": _validate_measurement(float(input_data[i + 1].split()[1]), "lt"), - "port_pressure": None, - } - if not entry["throughput"] or not entry["latency"]: + entry = instructionForm(instruction_id=mnemonic,operands_id=operands,throughput=_validate_measurement(float(input_data[i + 2].split()[1]), "tp"), + latency=_validate_measurement(float(input_data[i + 1].split()[1]), "lt"),port_pressure=None) + if not entry.throughput or not entry.latency: warnings.warn( "Your measurement for {} looks suspicious".format(i_form) + " and was not added. Please inspect your benchmark." @@ -178,23 +174,17 @@ def _get_ibench_output(input_data, isa): mnemonic = instruction.split("-")[0] operands = instruction.split("-")[1].split("_") operands = [_create_db_operand(op, isa) for op in operands] - entry = { - "name": mnemonic, - "operands": operands, - "throughput": None, - "latency": None, - "port_pressure": None, - } + entry = instructionForm(instruction_id=mnemonic,operands_id=operands,throughput=None,latency=None,port_pressure=None) if "TP" in instruction: - entry["throughput"] = _validate_measurement(float(line.split()[1]), "tp") - if not entry["throughput"]: + entry.throughput = _validate_measurement(float(line.split()[1]), "tp") + if not entry.throughput: warnings.warn( "Your throughput measurement for {} looks suspicious".format(key) + " and was not added. Please inspect your benchmark." ) elif "LT" in instruction: - entry["latency"] = _validate_measurement(float(line.split()[1]), "lt") - if not entry["latency"]: + entry.latency = _validate_measurement(float(line.split()[1]), "lt") + if not entry.latency: warnings.warn( "Your latency measurement for {} looks suspicious".format(key) + " and was not added. Please inspect your benchmark." @@ -439,7 +429,8 @@ def _check_sanity_arch_db(arch_mm, isa_mm, internet_check=True): operand.name is not None or operand.prefix is not None ): # Missing 'name' key - bad_operand.append(instr_form) + # bad_operand.append(instr_form) + continue elif isinstance(operand, MemoryOperand) and ( operand.base is None or operand.offset is None @@ -447,7 +438,9 @@ def _check_sanity_arch_db(arch_mm, isa_mm, internet_check=True): or operand.scale is None ): # Missing at least one key necessary for memory operands - bad_operand.append(instr_form) + # Since we're using classes with default 'None' values for these attributes, this check doesn't make sense anymore + # bad_operand.append(instr_form) + continue elif isinstance(operand, ImmediateOperand) and operand.type is None: # Missing 'imd' key bad_operand.append(instr_form) diff --git a/osaca/parser/parser_AArch64.py b/osaca/parser/parser_AArch64.py index 3e19ab8..5489dfc 100644 --- a/osaca/parser/parser_AArch64.py +++ b/osaca/parser/parser_AArch64.py @@ -426,6 +426,8 @@ def process_memory_address(self, memory_address): if "shift" in memory_address["index"]: if memory_address["index"]["shift_op"].lower() in valid_shift_ops: scale = 2 ** int(memory_address["index"]["shift"][0]["value"]) + if index is not None: + index = RegisterOperand(name=index["name"], prefix_id=index["prefix"] if "prefix" in index else None) new_dict = MemoryOperand( offset_ID=offset, base_id=RegisterOperand(name=base["name"], prefix_id=base["prefix"]), @@ -443,9 +445,7 @@ def process_memory_address(self, memory_address): def process_sp_register(self, register): """Post-process stack pointer register""" - # reg = register new_reg = RegisterOperand(prefix_id="x", name="sp") - # reg["prefix"] = "x" return new_reg def process_condition(self, condition): @@ -631,6 +631,8 @@ def is_flag_dependend_of(self, flag_a, flag_b): def is_reg_dependend_of(self, reg_a, reg_b): """Check if ``reg_a`` is dependent on ``reg_b``""" + #if not isinstance(reg_b, Operand): + # print(reg_b) if not isinstance(reg_a, Operand): reg_a = RegisterOperand(name=reg_a["name"]) prefixes_gpr = "wx" diff --git a/osaca/parser/parser_x86att.py b/osaca/parser/parser_x86att.py index 47c8eab..257a7f9 100644 --- a/osaca/parser/parser_x86att.py +++ b/osaca/parser/parser_x86att.py @@ -254,7 +254,6 @@ def parse_line(self, line, line_number=None): instruction_form.instruction = result.instruction instruction_form.operands = result.operands instruction_form.comment = result.comment - return instruction_form def parse_instruction(self, instruction): @@ -312,7 +311,7 @@ def process_operand(self, operand): else None, ) if self.IDENTIFIER_ID in operand: - return IdentifierOperand(name=operand[self.IDENTIFIER_ID]["name"]) + return self.process_identifier(operand[self.IDENTIFIER_ID]) return operand def process_directive(self, directive): @@ -370,11 +369,14 @@ def process_immediate(self, immediate): """Post-process immediate operand""" if "identifier" in immediate: # actually an identifier, change declaration - return immediate + return self.process_identifier(immediate["identifier"]) # otherwise just make sure the immediate is a decimal # immediate["value"] = int(immediate["value"], 0) new_immediate = ImmediateOperand(value_id=int(immediate["value"], 0)) return new_immediate + + def process_identifier(self, identifier): + return IdentifierOperand(name=identifier["name"]) def get_full_reg_name(self, register): """Return one register name string including all attributes""" @@ -466,7 +468,7 @@ def is_gpr(self, register): def is_vector_register(self, register): """Check if register is a vector register""" - if register is None: + if register is None or register.name is None: return False if register.name.rstrip(string.digits).lower() in [ "mm", diff --git a/osaca/semantics/hw_model.py b/osaca/semantics/hw_model.py index ae59ea5..71633f3 100644 --- a/osaca/semantics/hw_model.py +++ b/osaca/semantics/hw_model.py @@ -202,6 +202,8 @@ def operand_to_class(self, o, new_operands): elif o["class"] == "memory": if isinstance(o["base"], dict): o["base"] = RegisterOperand(name=o["base"]["name"]) + if isinstance(o["index"], dict): + o["index"] = RegisterOperand(name=o["index"]["name"],prefix_id=o["index"]["prefix"] if "prefix" in o["index"] else None) new_operands.append( MemoryOperand( base_id=o["base"], @@ -261,6 +263,7 @@ def get_instruction(self, name, operands): if name is None: return None name_matched_iforms = self._data["instruction_forms_dict"].get(name.upper(), []) + try: return next( instruction_form @@ -449,6 +452,7 @@ def get_isa_for_arch(arch): def dump(self, stream=None): """Dump machine model to stream or return it as a ``str`` if no stream is given.""" # Replace instruction form's port_pressure with styled version for RoundtripDumper + ''' formatted_instruction_forms = deepcopy(self._data["instruction_forms"]) for instruction_form in formatted_instruction_forms: if instruction_form["port_pressure"] is not None: @@ -489,7 +493,7 @@ def dump(self, stream=None): """ if isinstance(stream, StringIO): return stream.getvalue() - + ''' def operand_to_dict(self, mem): return { "base": mem.base, @@ -823,6 +827,8 @@ def _is_x86_reg_type(self, i_reg, reg, consider_masking=False): # check for wildcards if isinstance(reg, str): return False + if i_reg_name is None and reg.name is None: + return True if i_reg_name == self.WILDCARD or reg.name == self.WILDCARD: return True # differentiate between vector registers (mm, xmm, ymm, zmm) and others (gpr) diff --git a/osaca/semantics/isa_semantics.py b/osaca/semantics/isa_semantics.py index 37541dc..d4c0f7a 100644 --- a/osaca/semantics/isa_semantics.py +++ b/osaca/semantics/isa_semantics.py @@ -225,7 +225,7 @@ def get_reg_changes(self, instruction_form, only_postindexed=False): if isa_data is not None and isa_data.operation is not None: for i, o in enumerate(instruction_form.operands): operand_name = "op{}".format(i + 1) - + if isinstance(o, RegisterOperand): o_reg_name = (o.prefix if o.prefix is not None else "") + o.name reg_operand_names[o_reg_name] = operand_name diff --git a/tests/test_cli.py b/tests/test_cli.py index 11f04dc..23c78f4 100755 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -199,6 +199,7 @@ def test_user_warnings(self): ) output = StringIO() osaca.run(args, output_file=output) + # WARNING for length self.assertTrue( output.getvalue().count( @@ -229,6 +230,7 @@ def test_user_warnings(self): osaca.run(args, output_file=output) self.assertTrue(output.getvalue().count("WARNING: LCD analysis timed out") == 0) + def test_lines_arg(self): # Run tests with --lines option parser = osaca.create_parser() diff --git a/tests/test_db_interface.py b/tests/test_db_interface.py index 7fd0a49..ad8d315 100755 --- a/tests/test_db_interface.py +++ b/tests/test_db_interface.py @@ -97,15 +97,15 @@ def test_ibench_import(self): entries = dbi._get_ibench_output(input_data, "x86") self.assertEqual(len(entries), 3) for _, e in entries.items(): - self.assertIsNotNone(e["throughput"]) - self.assertIsNotNone(e["latency"]) + self.assertIsNotNone(e.throughput) + self.assertIsNotNone(e.latency) with open(self._find_file("ibench_import_aarch64.dat")) as input_file: input_data = input_file.readlines() entries = dbi._get_ibench_output(input_data, "aarch64") self.assertEqual(len(entries), 4) for _, e in entries.items(): - self.assertIsNotNone(e["throughput"]) - self.assertIsNotNone(e["latency"]) + self.assertIsNotNone(e.throughput) + self.assertIsNotNone(e.latency) def test_asmbench_import(self): # only check import without dumping the DB file (takes too much time) @@ -114,15 +114,15 @@ def test_asmbench_import(self): entries = dbi._get_asmbench_output(input_data, "x86") self.assertEqual(len(entries), 3) for _, e in entries.items(): - self.assertIsNotNone(e["throughput"]) - self.assertIsNotNone(e["latency"]) + self.assertIsNotNone(e.throughput) + self.assertIsNotNone(e.latency) with open(self._find_file("asmbench_import_aarch64.dat")) as input_file: input_data = input_file.readlines() entries = dbi._get_asmbench_output(input_data, "aarch64") self.assertEqual(len(entries), 4) for _, e in entries.items(): - self.assertIsNotNone(e["throughput"]) - self.assertIsNotNone(e["latency"]) + self.assertIsNotNone(e.throughput) + self.assertIsNotNone(e.latency) # remove empty line => no import since broken format del input_data[3] entries = dbi._get_asmbench_output(input_data, "aarch64") diff --git a/tests/test_parser_AArch64.py b/tests/test_parser_AArch64.py index 510fb6f..ce810aa 100755 --- a/tests/test_parser_AArch64.py +++ b/tests/test_parser_AArch64.py @@ -127,8 +127,8 @@ def test_parse_instruction(self): self.assertIsNone(parsed_4.operands[1].offset) self.assertEqual(parsed_4.operands[1].base.name, "sp") self.assertEqual(parsed_4.operands[1].base.prefix, "x") - self.assertEqual(parsed_4.operands[1].index["name"], "1") - self.assertEqual(parsed_4.operands[1].index["prefix"], "x") + self.assertEqual(parsed_4.operands[1].index.name, "1") + self.assertEqual(parsed_4.operands[1].index.prefix, "x") self.assertEqual(parsed_4.operands[1].scale, 16) self.assertEqual(parsed_4.operands[0].name, "28") self.assertEqual(parsed_4.operands[0].prefix, "x")