From df8a81bf4dee1919b789092451d65137ba0ec865 Mon Sep 17 00:00:00 2001 From: JanLJL Date: Fri, 23 Aug 2019 19:01:20 +0200 Subject: [PATCH] initial version of loop-carried dependencies checker --- osaca/frontend.py | 12 ++++++ osaca/parser/attr_dict.py | 18 +++++--- osaca/parser/base_parser.py | 15 +++++-- osaca/semantics/hw_model.py | 6 ++- osaca/semantics/kernel_dg.py | 79 ++++++++++++++++++++++++++++++++---- 5 files changed, 112 insertions(+), 18 deletions(-) diff --git a/osaca/frontend.py b/osaca/frontend.py index 0806661..baff3d9 100755 --- a/osaca/frontend.py +++ b/osaca/frontend.py @@ -134,6 +134,18 @@ class Frontend(object): ) ) + def print_loopcarried_dependencies(self, dep_tuplelist, separator='|'): + print('\n\n------------------------') + for tup in dep_tuplelist: + print( + '{}: {} {} {}'.format( + tup[0]['line_number'], + tup[0]['line'], + separator, + [node['line_number'] for node in tup[1]], + ) + ) + def print_list_summary(self): raise NotImplementedError diff --git a/osaca/parser/attr_dict.py b/osaca/parser/attr_dict.py index 0230982..0e04197 100755 --- a/osaca/parser/attr_dict.py +++ b/osaca/parser/attr_dict.py @@ -8,8 +8,16 @@ class AttrDict(dict): @staticmethod def convert_dict(dictionary): - for key in list(dictionary.keys()): - entry = dictionary[key] - if isinstance(entry, type(dict())) or isinstance(entry, type(AttrDict())): - dictionary[key] = AttrDict.convert_dict(dictionary[key]) - return AttrDict(dictionary) + if isinstance(dictionary, type(list())): + return [AttrDict.convert_dict(x) for x in dictionary] + if isinstance(dictionary, type(dict())): + for key in list(dictionary.keys()): + entry = dictionary[key] + if isinstance(entry, type(dict())) or isinstance( + entry, type(AttrDict()) + ): + dictionary[key] = AttrDict.convert_dict(dictionary[key]) + if isinstance(entry, type(list())): + dictionary[key] = [AttrDict.convert_dict(x) for x in entry] + return AttrDict(dictionary) + return dictionary diff --git a/osaca/parser/base_parser.py b/osaca/parser/base_parser.py index 577d319..edfe8d1 100755 --- a/osaca/parser/base_parser.py +++ b/osaca/parser/base_parser.py @@ -35,14 +35,23 @@ class BaseParser(object): def parse_line(self, line, line_number): # Done in derived classes - raise NotImplementedError() + raise NotImplementedError def parse_instruction(self, instruction): # Done in derived classes - raise NotImplementedError() + raise NotImplementedError + + def parse_register(self, register_string): + raise NotImplementedError + + def is_gpr(self, register): + raise NotImplementedError + + def is_vector_register(self, register): + raise NotImplementedError def construct_parser(self): - raise NotImplementedError() + raise NotImplementedError ################## # Helper functions diff --git a/osaca/semantics/hw_model.py b/osaca/semantics/hw_model.py index 878d0c2..7216346 100755 --- a/osaca/semantics/hw_model.py +++ b/osaca/semantics/hw_model.py @@ -50,11 +50,13 @@ class MachineModel(object): ###################################################### def get_instruction(self, name, operands): + if name is None: + return None try: return next( instruction_form for instruction_form in self._data['instruction_forms'] - if instruction_form['name'] == name + if instruction_form['name'].lower() == name.lower() and self._match_operands(instruction_form['operands'], operands) ) except StopIteration: @@ -75,7 +77,7 @@ class MachineModel(object): matches = [ instruction_form for instruction_form in self._data['instruction_forms'] - if instruction_form['name'] == name + if instruction_form['name'].lower() == name.lower() and self._match_operands(instruction_form['operands'], operands) ] if len(matches) > 1: diff --git a/osaca/semantics/kernel_dg.py b/osaca/semantics/kernel_dg.py index f9f6915..3f5a581 100755 --- a/osaca/semantics/kernel_dg.py +++ b/osaca/semantics/kernel_dg.py @@ -1,7 +1,11 @@ #!/usr/bin/env python3 +import copy + import networkx as nx +from osaca.parser import AttrDict + from .hw_model import MachineModel @@ -10,19 +14,63 @@ class KernelDG(nx.DiGraph): self.kernel = parsed_kernel self.parser = parser self.model = hw_model - self.dg = self.create_DG() + self.dg = self.create_DG(self.kernel) + self.loopcarried_deps = self.check_for_loopcarried_dep(self.kernel) - def check_for_loop(self, kernel): - raise NotImplementedError + def check_for_loopcarried_dep(self, kernel): + # 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 * 10 + # get dependency graph + dg = self.create_DG(tmp_kernel) + descendants = [ + (x, sorted([x for x in nx.algorithms.dag.descendants(dg, x)])) + for x in range(first_line_no, first_line_no + kernel_length) + if x in dg + ] + loopcarried_deps = [ + x for x in descendants if len(x[1]) > 0 and x[1][-1] >= first_line_no * 10 + ] - def create_DG(self): + # adjust line numbers + # and add reference to kernel again + for i, dep in enumerate(loopcarried_deps): + nodes = [int(n / 10) for n in dep[1] if n >= first_line_no * 10] + nodes = [self._get_node_by_lineno(x) for x in nodes] + loopcarried_deps[i] = (self._get_node_by_lineno(dep[0]), nodes) + # check if dependency is cyclic + cyclic_lc_deps = [] + for dep in loopcarried_deps: + write_back = list( + self.find_depending( + dep[0], + tmp_kernel[dep[0].line_number - first_line_no + 1:], + include_write=True, + ) + ) + if ( + write_back is not None + and len(write_back) > 0 + and int(write_back[-1].line_number / 10) == dep[0].line_number + ): + cyclic_lc_deps.append(dep) + return cyclic_lc_deps + + def _get_node_by_lineno(self, lineno): + return [instr for instr in self.kernel if instr.line_number == lineno][0] + + def create_DG(self, kernel): # 1. go through kernel instruction forms (as vertices) # 2. find edges (to dependend further instruction) # 3. get LT value and set as edge weight # 4. add instr forms as node attribute dg = nx.DiGraph() - for i, instruction_form in enumerate(self.kernel): - for dep in self.find_depending(instruction_form, self.kernel[i + 1:]): + for i, instruction_form in enumerate(kernel): + for dep in self.find_depending(instruction_form, kernel[i + 1:]): dg.add_edge( instruction_form['line_number'], dep['line_number'], @@ -38,9 +86,16 @@ class KernelDG(nx.DiGraph): return [x for x in self.kernel if x['line_number'] in longest_path] else: # split to DAG - raise NotImplementedError + raise NotImplementedError('Kernel is cyclic.') - def find_depending(self, instruction_form, kernel): + def get_loopcarried_dependencies(self): + if nx.algorithms.dag.is_directed_acyclic_graph(self.dg): + return self.loopcarried_deps + else: + # split to DAG + raise NotImplementedError('Kernel is cyclic.') + + def find_depending(self, instruction_form, kernel, include_write=False): if instruction_form.operands is None: return for dst in instruction_form.operands.destination + instruction_form.operands.src_dst: @@ -51,8 +106,12 @@ class KernelDG(nx.DiGraph): yield instr_form if self.is_written(dst.register, instr_form): # operand in src_dst list + if include_write: + yield instr_form break elif self.is_written(dst.register, instr_form): + if include_write: + yield instr_form break elif 'memory' in dst: # Check if base register is altered during memory access @@ -63,8 +122,12 @@ class KernelDG(nx.DiGraph): yield instr_form if self.is_written(dst.memory.base, instr_form): # operand in src_dst list + if include_write: + yield instr_form break elif self.is_written(dst.memory.base, instr_form): + if include_write: + yield instr_form break def get_dependent_instruction_forms(self, instr_form=None, line_number=None):