From 75393f106c6a8a35720f39c0967e0dba39f40fa9 Mon Sep 17 00:00:00 2001 From: JanLJL Date: Mon, 8 Jul 2019 15:47:08 +0200 Subject: [PATCH] finished refactoring for semanticsAppender, including first tests --- osaca/data/isa/AArch64.yml | 16 ++--- osaca/data/isa/x86.yml | 14 ++-- osaca/semantics/hw_model.py | 8 +-- osaca/semantics/semanticsAppender.py | 68 ++++++++++++++++--- tests/all_tests.py | 2 +- ...dependency_finder.py => test_semantics.py} | 52 ++++++++++++-- 6 files changed, 126 insertions(+), 34 deletions(-) rename tests/{test_dependency_finder.py => test_semantics.py} (60%) diff --git a/osaca/data/isa/AArch64.yml b/osaca/data/isa/AArch64.yml index 8aa8133..a67171a 100644 --- a/osaca/data/isa/AArch64.yml +++ b/osaca/data/isa/AArch64.yml @@ -45,11 +45,11 @@ instruction_forms: prefix: "d" source: false destination: true - - class: "register" + - class: "register" prefix: "d" source: false destination: true - - class: "memory" + - class: "memory" base: "x" offset: "imd" index: ~ @@ -64,11 +64,11 @@ instruction_forms: prefix: "q" source: false destination: true - - class: "register" + - class: "register" prefix: "q" source: false destination: true - - class: "memory" + - class: "memory" base: "x" offset: "imd" index: ~ @@ -83,11 +83,11 @@ instruction_forms: prefix: "d" source: true destination: false - - class: "register" + - class: "register" prefix: "d" source: true destination: false - - class: "memory" + - class: "memory" base: "x" offset: "imd" index: ~ @@ -102,11 +102,11 @@ instruction_forms: prefix: "q" source: true destination: false - - class: "register" + - class: "register" prefix: "q" source: true destination: false - - class: "memory" + - class: "memory" base: "x" offset: "imd" index: ~ diff --git a/osaca/data/isa/x86.yml b/osaca/data/isa/x86.yml index b72a191..d2f7acf 100644 --- a/osaca/data/isa/x86.yml +++ b/osaca/data/isa/x86.yml @@ -48,10 +48,10 @@ instruction_forms: - name: vaddsd operands: - class: "register" - name: "xmm" - source: true - destination: true - - class: "register" - name: "xmm" - source: true - destination: false + name: "xmm" + source: true + destination: true + - class: "register" + name: "xmm" + source: true + destination: false diff --git a/osaca/semantics/hw_model.py b/osaca/semantics/hw_model.py index 9e9ebe9..c7fcf25 100755 --- a/osaca/semantics/hw_model.py +++ b/osaca/semantics/hw_model.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os -from osaca.parser.parser_x86att import ParserX86ATT +from osaca.parser import ParserX86ATT from ruamel import yaml @@ -34,7 +34,7 @@ class MachineModel(object): def _find_file(self, name): data_dir = os.path.expanduser('~/.osaca/data') - name = os.path.join(data_dir, name) + name = os.path.join(data_dir, name + '.yml') assert os.path.exists(name) return name @@ -92,7 +92,7 @@ class MachineModel(object): if 'memory' in operand: if i_operand['class'] != 'memory': return False - return self._is_AArch64_mem_type(i_operand['memory'], operand['memory']) + return self._is_AArch64_mem_type(i_operand, operand['memory']) # immediate if 'value' in operand: return i_operand['class'] == 'immediate' and i_operand['imd'] == 'int' @@ -116,7 +116,7 @@ class MachineModel(object): if 'memory' in operand: if i_operand['class'] != 'memory': return False - return self._is_x86_mem_type(i_operand['memory'], operand['memory']) + return self._is_x86_mem_type(i_operand, operand['memory']) # immediate if 'value' in operand: return i_operand['class'] == 'immediate' and i_operand['imd'] == 'int' diff --git a/osaca/semantics/semanticsAppender.py b/osaca/semantics/semanticsAppender.py index db395c9..8df85a3 100755 --- a/osaca/semantics/semanticsAppender.py +++ b/osaca/semantics/semanticsAppender.py @@ -2,6 +2,7 @@ import os +from osaca.parser import AttrDict from .hw_model import MachineModel @@ -21,20 +22,71 @@ class SemanticsAppender(object): # - source # - destination # - source/destination - - # for this, have a default implementation and get exceptions from generic x86/AArch64 ISA file def assign_src_dst(self, instruction_form): + # if the instruction form doesn't have operands, there's nothing to do + if instruction_form['operands'] is None: + 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['name'], instruction_form['operands'] + instruction_form['instruction'], instruction_form['operands'] ) + operands = instruction_form['operands'] + op_dict = {} if isa_data is None: # no irregular operand structure, apply default - # TODO - pass + op_dict['source'] = self._get_regular_source_operands(instruction_form) + op_dict['destination'] = self._get_regular_destination_operands(instruction_form) + op_dict['src_dst'] = [] else: # load src/dst structure from isa_data - # TODO - pass - raise NotImplementedError + 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 + # store operand list in dict and reassign operand key/value pair + op_dict['operand_list'] = operands + instruction_form['operands'] = AttrDict.convert_dict(op_dict) + + def _get_regular_source_operands(self, instruction_form): + if self._isa == 'x86': + return self._get_regular_source_x86ATT(instruction_form) + if self._isa == 'AArch64': + return self._get_regular_source_AArch64(instruction_form) + + def _get_regular_destination_operands(self, instruction_form): + if self._isa == 'x86': + return self._get_regular_destination_x86ATT(instruction_form) + if self._isa == 'AArch64': + return self._get_regular_destination_AArch64(instruction_form) + + def _get_regular_source_x86ATT(self, instruction_form): + # return all but last operand + sources = [ + op for op in instruction_form['operands'][0:len(instruction_form['operands']) - 1] + ] + return sources + + def _get_regular_source_AArch64(self, instruction_form): + # return all but first operand + sources = [ + op for op in instruction_form['operands'][1:len(instruction_form['operands'])] + ] + return sources + + def _get_regular_destination_x86ATT(self, instruction_form): + # return last operand + return instruction_form['operands'][-1:] + + def _get_regular_destination_AArch64(self, instruction_form): + # return first operand + return instruction_form['operands'][:1] diff --git a/tests/all_tests.py b/tests/all_tests.py index 10737c9..2d88661 100755 --- a/tests/all_tests.py +++ b/tests/all_tests.py @@ -9,7 +9,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'test_parser_x86att', 'test_parser_AArch64v81', 'test_marker_utils', -# 'test_dependency_finder' + 'test_semantics' ] ) diff --git a/tests/test_dependency_finder.py b/tests/test_semantics.py similarity index 60% rename from tests/test_dependency_finder.py rename to tests/test_semantics.py index c886964..3920afe 100755 --- a/tests/test_dependency_finder.py +++ b/tests/test_semantics.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 """ -Unit tests for KernelDAG +Unit tests for Semantic Analysis """ import os import unittest -from osaca.dependency_finder import KernelDAG from osaca.parser import AttrDict, ParserAArch64v81, ParserX86ATT +from osaca.semantics.kernel_dg import KernelDG +from osaca.semantics.hw_model import MachineModel +from osaca.semantics.semanticsAppender import SemanticsAppender -class TestDependencyFinder(unittest.TestCase): +class TestSemanticTools(unittest.TestCase): def setUp(self): self.parser_x86 = ParserX86ATT() self.parser_AArch64 = ParserAArch64v81() @@ -21,23 +23,54 @@ class TestDependencyFinder(unittest.TestCase): self.kernel_x86 = self.parser_x86.parse_file(code_x86) self.kernel_AArch64 = self.parser_AArch64.parse_file(code_AArch64) + self.machine_model_csl = MachineModel('csl') + self.machine_model_tx2 = MachineModel('vulcan') + self.semantics_csl = SemanticsAppender(self.machine_model_csl) + self.semantics_tx2 = SemanticsAppender(self.machine_model_tx2) + for i in range(len(self.kernel_x86)): + self.semantics_csl.assign_src_dst(self.kernel_x86[i]) + for i in range(len(self.kernel_AArch64)): + self.semantics_tx2.assign_src_dst(self.kernel_AArch64[i]) + ########### # Tests ########### + def test_src_dst_assignment_x86(self): + for instruction_form in self.kernel_x86: + with self.subTest(instruction_form=instruction_form): + if instruction_form['operands'] is not None: + self.assertTrue('source' in instruction_form['operands']) + self.assertTrue('destination' in instruction_form['operands']) + self.assertTrue('src_dst' in instruction_form['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['operands'] is not None: + self.assertTrue('source' in instruction_form['operands']) + self.assertTrue('destination' in instruction_form['operands']) + self.assertTrue('src_dst' in instruction_form['operands']) + def test_is_read_is_written_x86(self): # independent form HW model - dag = KernelDAG(self.kernel_x86, self.parser_x86, None) + dag = KernelDG(self.kernel_x86, self.parser_x86, None) reg_rcx = AttrDict({'name': 'rcx'}) reg_ymm1 = AttrDict({'name': 'ymm1'}) instr_form_r_c = self.parser_x86.parse_line('vmovsd %xmm0, (%r15,%rcx,8)') + self.semantics_csl.assign_src_dst(instr_form_r_c) instr_form_non_r_c = self.parser_x86.parse_line('movl %xmm0, (%r15,%rax,8)') + self.semantics_csl.assign_src_dst(instr_form_non_r_c) instr_form_w_c = self.parser_x86.parse_line('movi $0x05ACA, %rcx') + self.semantics_csl.assign_src_dst(instr_form_w_c) instr_form_rw_ymm_1 = self.parser_x86.parse_line('vinsertf128 $0x1, %xmm1, %ymm0, %ymm1') + self.semantics_csl.assign_src_dst(instr_form_rw_ymm_1) instr_form_rw_ymm_2 = self.parser_x86.parse_line('vinsertf128 $0x1, %xmm0, %ymm1, %ymm1') + self.semantics_csl.assign_src_dst(instr_form_rw_ymm_2) instr_form_r_ymm = self.parser_x86.parse_line('vmovapd %ymm1, %ymm0') + self.semantics_csl.assign_src_dst(instr_form_r_ymm) self.assertTrue(dag.is_read(reg_rcx, instr_form_r_c)) self.assertFalse(dag.is_read(reg_rcx, instr_form_non_r_c)) @@ -54,19 +87,26 @@ class TestDependencyFinder(unittest.TestCase): def test_is_read_is_written_AArch64(self): # independent form HW model - dag = KernelDAG(self.kernel_AArch64, self.parser_AArch64, None) + dag = KernelDG(self.kernel_AArch64, self.parser_AArch64, None) reg_x1 = AttrDict({'prefix': 'x', 'name': '1'}) reg_q1 = AttrDict({'prefix': 'q', 'name': '1'}) reg_v1 = AttrDict({'prefix': 'v', 'name': '1', 'lanes': '2', 'shape': 'd'}) regs = [reg_x1, reg_q1, reg_v1] instr_form_r_1 = self.parser_AArch64.parse_line('stp q1, q3, [x12, #192]') + self.semantics_tx2.assign_src_dst(instr_form_r_1) instr_form_r_2 = self.parser_AArch64.parse_line('fadd v2.2d, v1.2d, v0.2d') + self.semantics_tx2.assign_src_dst(instr_form_r_2) instr_form_w_1 = self.parser_AArch64.parse_line('ldr x0, [x0, #:got_lo12:q2c]') + self.semantics_tx2.assign_src_dst(instr_form_w_1) instr_form_rw_1 = self.parser_AArch64.parse_line('fmul v1.2d, v1.2d, v0.2d') + self.semantics_tx2.assign_src_dst(instr_form_rw_1) instr_form_rw_2 = self.parser_AArch64.parse_line('ldp q2, q4, [x1, #64]!') + self.semantics_tx2.assign_src_dst(instr_form_rw_2) instr_form_rw_3 = self.parser_AArch64.parse_line('str x4, [x1], #64') + self.semantics_tx2.assign_src_dst(instr_form_rw_3) instr_form_non_rw_1 = self.parser_AArch64.parse_line('adds x0, x11') + self.semantics_tx2.assign_src_dst(instr_form_non_rw_1) for reg in regs: with self.subTest(reg=reg): @@ -95,5 +135,5 @@ class TestDependencyFinder(unittest.TestCase): if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(TestDependencyFinder) + suite = unittest.TestLoader().loadTestsFromTestCase(TestSemanticTools) unittest.TextTestRunner(verbosity=2).run(suite)