mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2026-01-06 19:20:07 +01:00
initial version of loop-carried dependencies checker
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user