From 34437e34c5a88cde77e7cbdc175528cbe976b0ab Mon Sep 17 00:00:00 2001
From: Rafael Ravedutti <rafaelravedutti@gmail.com>
Date: Tue, 29 Mar 2022 23:32:51 +0200
Subject: [PATCH] Add device analysis, Makefile and requirements

Signed-off-by: Rafael Ravedutti <rafaelravedutti@gmail.com>
---
 Makefile                             | 17 +++++
 requirements.txt                     |  0
 src/pairs/analysis/devices.py        | 66 +++++++++++++++++++
 src/pairs/ir/kernel.py               | 97 ++++++++++++++++++++++++++++
 src/pairs/ir/module.py               |  2 +-
 src/pairs/transformations/devices.py | 22 +++++++
 6 files changed, 203 insertions(+), 1 deletion(-)
 create mode 100644 Makefile
 create mode 100644 requirements.txt
 create mode 100644 src/pairs/analysis/devices.py
 create mode 100644 src/pairs/ir/kernel.py

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..aaf7dbf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+.PHONY: all build clean lj_ns
+
+all: build lj_ns clean
+	@echo "Everything was done!"
+
+build:
+	@echo "Building pairs package..."
+	python3 setup.py build && python3 setup.py install --user
+
+lj_ns:
+	@echo "Generating and compiling CPP for Lennard-Jones example..."
+	python3 examples/lj_func.py
+	g++ -o lj_ns lj_ns.cpp
+
+clean:
+	@echo "Cleaning..."
+	rm -rf build lj_ns lj_ns.cpp dist pairs.egg-info kernels kernels.pdf
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e69de29
diff --git a/src/pairs/analysis/devices.py b/src/pairs/analysis/devices.py
new file mode 100644
index 0000000..94d13ba
--- /dev/null
+++ b/src/pairs/analysis/devices.py
@@ -0,0 +1,66 @@
+from pairs.ir.visitor import Visitor
+
+
+class FetchKernelReferences(Visitor):
+    def __init__(self, ast):
+        super().__init__(ast)
+        self.kernel_stack = []
+        self.kernel_decls = {}
+        self.kernel_used_bin_ops = {}
+        self.writing = False
+
+    def visit_ArrayAccess(self, ast_node):
+        # Visit array and save current writing state
+        self.visit(ast_node.array)
+        writing_state = self.writing
+
+        # Index elements are read-only
+        self.writing = False
+        self.visit([roc for roc in ast_node.children() if roc != ast_node.array])
+        self.writing = writing_state
+
+    def visit_Assign(self, ast_node):
+        self.writing = True
+        self.visit(ast_node.destinations())
+        self.writing = False
+        self.visit(ast_node.sources())
+
+    def visit_Kernel(self, ast_node):
+        kernel_id = ast_node.kernel_id
+        self.kernel_decls[kernel_id] = []
+        self.kernel_used_bin_ops[kernel_id] = []
+        self.kernel_stack.append(ast_node)
+        self.visit_children(ast_node)
+        self.kernel_stack.pop()
+        ast_node.add_bin_op([b for b in self.kernel_used_bin_ops[kernel_id] if b not in self.kernel_decls[kernel_id]])
+
+    def visit_PropertyAccess(self, ast_node):
+        # Visit property and save current writing state
+        self.visit(ast_node.prop)
+        writing_state = self.writing
+
+        # Index elements are read-only
+        self.writing = False
+        self.visit([roc for roc in ast_node.children() if roc != ast_node.prop])
+        self.writing = writing_state
+
+    def visit_Decl(self, ast_node):
+        if isinstance(ast_node.elem, BinOp):
+            for k in self.kernel_stack:
+                self.kernel_decls[k.kernel_id].append(ast_node)
+
+    def visit_BinOp(self, ast_node):
+        for k in self.kernel_stack:
+            self.kernel_used_bin_ops[k.kernel_id].append(ast_node)
+
+    def visit_Array(self, ast_node):
+        for k in self.kernel_stack:
+            k.add_array(ast_node, self.writing)
+
+    def visit_Property(self, ast_node):
+        for k in self.kernel_stack:
+            k.add_property(ast_node, self.writing)
+
+    def visit_Var(self, ast_node):
+        for k in self.kernel_stack:
+            k.add_variable(ast_node, self.writing)
diff --git a/src/pairs/ir/kernel.py b/src/pairs/ir/kernel.py
new file mode 100644
index 0000000..8f7770c
--- /dev/null
+++ b/src/pairs/ir/kernel.py
@@ -0,0 +1,97 @@
+from pairs.ir.arrays import Array
+from pairs.ir.ast_node import ASTNode
+from pairs.ir.properties import Property
+from pairs.ir.variables import Var
+
+
+class Kernel(ASTNode):
+    last_kernel = 0
+
+    def __init__(self, sim, name=None, block=None):
+        super().__init__(sim)
+        self._id = Kernel.last_kernel
+        self._name = name if name is not None else "kernel" + str(Kernel.last_kernel)
+        self._variables = {}
+        self._arrays = {}
+        self._properties = {}
+        self._bin_ops = []
+        self._block = block
+        sim.add_kernel(self)
+        Kernel.last_kernel += 1
+
+    @property
+    def kernel_id(self):
+        return self._id
+
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def block(self):
+        return self._block
+
+    def variables(self):
+        return self._variables
+
+    def read_only_variables(self):
+        return [v for v in self._variables if 'w' not in self._variables[v]]
+
+    def write_variables(self):
+        return [v for v in self._variables if 'w' in self._variables[v]]
+
+    def arrays(self):
+        return self._arrays
+
+    def properties(self):
+        return self._properties
+
+    def properties_to_synchronize(self):
+        return {p for p in self._properties if self._properties[p][0] == 'r'}
+
+    def write_properties(self):
+        return {p for p in self._properties if 'w' in self._properties[p]}
+
+    def add_array(self, array, write=False):
+        array_list = array if isinstance(array, list) else [array]
+        character = 'w' if write else 'r'
+        for a in array_list:
+            assert isinstance(a, Array), "Kernel.add_array(): given element is not of type Array!"
+            self._arrays[a] = character if a not in self._arrays else self._arrays[a] + character
+
+    def add_variable(self, variable, write=False):
+        variable_list = variable if isinstance(variable, list) else [variable]
+        character = 'w' if write else 'r'
+        for v in variable_list:
+            assert isinstance(v, Var), "Kernel.add_variable(): given element is not of type Var!"
+            self._variables[v] = character if v not in self._variables else self._variables[v] + character
+
+    def add_property(self, prop, write=False):
+        prop_list = prop if isinstance(prop, list) else [prop]
+        character = 'w' if write else 'r'
+        for p in prop_list:
+            assert isinstance(p, Property), "Kernel.add_property(): given element is not of type Property!"
+            self._properties[p] = character if p not in self._properties else self._properties[p] + character
+
+    def add_bin_op(self, bin_op):
+        bin_op_list = bin_op if isinstance(bin_op, list) else [bin_op]
+        for b in bin_op_list:
+            assert isinstance(b, BinOp), "Kernel.add_bin_op(): given element is not of type BinOp!"
+            self._bin_ops.append(b)
+
+    def children(self):
+        return [self._block]
+
+
+class KernelLaunch(ASTNode):
+    def __init__(self, sim, kernel, iterator, range_min, range_max):
+        assert isinstance(module, Kernel), "KernelLaunch(): given parameter is not of type Kernel!"
+        super().__init__(sim)
+        self._kernel = kernel
+        self._iterator = iterator
+        self._range_min = range_min
+        self._range_max = range_max
+
+    @property
+    def kernel(self):
+        return self._kernel
diff --git a/src/pairs/ir/module.py b/src/pairs/ir/module.py
index d8ffcaf..7353ddd 100644
--- a/src/pairs/ir/module.py
+++ b/src/pairs/ir/module.py
@@ -9,7 +9,7 @@ class Module(ASTNode):
 
     def __init__(self, sim, name=None, block=None, resizes_to_check={}, check_properties_resize=False, run_on_device=False):
         super().__init__(sim)
-        self._name = name if name is not None else "module_" + str(Module.last_module)
+        self._name = name if name is not None else "module" + str(Module.last_module)
         self._variables = {}
         self._arrays = {}
         self._properties = {}
diff --git a/src/pairs/transformations/devices.py b/src/pairs/transformations/devices.py
index 5305081..1a9a037 100644
--- a/src/pairs/transformations/devices.py
+++ b/src/pairs/transformations/devices.py
@@ -62,3 +62,25 @@ class AddDeviceCopies(Mutator):
 
         ast_node.stmts = new_stmts
         return ast_node
+
+
+class AddDeviceKernels(Mutator):
+    def __init__(self, ast):
+        super().__init__(ast)
+
+    def mutate_Module(self, ast_node):
+        ast_node._block = self.mutate(ast_node._block)
+
+        if ast_node.run_on_device:
+            new_stmts = []
+            kernel_id = 0
+            for s in ast_node._block.stmts:
+                if s is not None:
+                    if isinstance(s, For) and (not isinstance(s.min, Lit) or not isinstance(s.max, Lit)):
+                        kernel = Kernel(ast_node.sim, f"{ast_node.name}_kernel{kernel_id}", s.block)
+                        new_stmts.append(KernelLaunch(ast_node.sim, kernel, s.iterator, s.min, s.max))
+                        kernel_id += 1
+                    else:
+                        new_stmts.append(s)
+
+        return ast_node
-- 
GitLab