From bd8b02d7dce8f49a58165629e3ff00ab9f78cfd9 Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Thu, 9 Nov 2023 10:55:51 +0900 Subject: [PATCH] renamed package. Started building test case for mdspans. --- .gitignore | 6 +- .../emitters/cpu/templates/BasicCpu.tmpl.h | 11 --- pystencils_sfg/source_concepts/containers.py | 20 ------ .../source_concepts/cpp/std_mdspan.py | 17 ----- .../source_concepts/source_concepts.py | 27 ------- pystencils_sfg/tree/__init__.py | 7 -- {pystencils_sfg => pystencilssfg}/__init__.py | 2 +- {pystencils_sfg => pystencilssfg}/context.py | 70 ++++++++++++------- .../emitters/__init__.py | 0 .../emitters/cpu/__init__.py | 0 .../emitters/cpu/basic_cpu.py | 19 +++-- .../emitters/cpu/jinja_filters.py | 3 +- .../emitters/cpu/templates/BasicCpu.tmpl.cpp | 2 - .../emitters/cpu/templates/BasicCpu.tmpl.h | 18 +++++ .../kernel_namespace.py | 3 + .../source_components.py | 2 +- .../source_concepts/__init__.py | 0 pystencilssfg/source_concepts/containers.py | 36 ++++++++++ .../source_concepts/cpp/std_mdspan.py | 43 ++++++++++++ .../source_concepts/source_concepts.py | 43 ++++++++++++ pystencilssfg/tree/__init__.py | 9 +++ .../tree/basic_nodes.py | 70 ++++++++++++------- .../tree/builders.py | 59 ++++++---------- .../tree/conditional.py | 0 .../tree/visitors.py | 4 +- tests/TestSequencing.py | 2 +- tests/mdspan/Makefile | 34 +++++++++ tests/mdspan/kernels.py | 8 +++ tests/mdspan/main.cpp | 6 ++ 29 files changed, 337 insertions(+), 184 deletions(-) delete mode 100644 pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.h delete mode 100644 pystencils_sfg/source_concepts/containers.py delete mode 100644 pystencils_sfg/source_concepts/cpp/std_mdspan.py delete mode 100644 pystencils_sfg/source_concepts/source_concepts.py delete mode 100644 pystencils_sfg/tree/__init__.py rename {pystencils_sfg => pystencilssfg}/__init__.py (99%) rename {pystencils_sfg => pystencilssfg}/context.py (65%) rename {pystencils_sfg => pystencilssfg}/emitters/__init__.py (100%) rename {pystencils_sfg => pystencilssfg}/emitters/cpu/__init__.py (100%) rename {pystencils_sfg => pystencilssfg}/emitters/cpu/basic_cpu.py (57%) rename {pystencils_sfg => pystencilssfg}/emitters/cpu/jinja_filters.py (88%) rename {pystencils_sfg => pystencilssfg}/emitters/cpu/templates/BasicCpu.tmpl.cpp (99%) create mode 100644 pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.h rename {pystencils_sfg => pystencilssfg}/kernel_namespace.py (98%) rename {pystencils_sfg => pystencilssfg}/source_components.py (95%) rename {pystencils_sfg => pystencilssfg}/source_concepts/__init__.py (100%) create mode 100644 pystencilssfg/source_concepts/containers.py create mode 100644 pystencilssfg/source_concepts/cpp/std_mdspan.py create mode 100644 pystencilssfg/source_concepts/source_concepts.py create mode 100644 pystencilssfg/tree/__init__.py rename {pystencils_sfg => pystencilssfg}/tree/basic_nodes.py (53%) rename {pystencils_sfg => pystencilssfg}/tree/builders.py (54%) rename {pystencils_sfg => pystencilssfg}/tree/conditional.py (100%) rename {pystencils_sfg => pystencilssfg}/tree/visitors.py (96%) create mode 100644 tests/mdspan/Makefile create mode 100644 tests/mdspan/kernels.py create mode 100644 tests/mdspan/main.cpp diff --git a/.gitignore b/.gitignore index 136ac3c..0788a35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ **/.vscode -**/__pycache__ \ No newline at end of file +**/__pycache__ +**/bin +**/obj +**/out +**/generated_src \ No newline at end of file diff --git a/pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.h b/pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.h deleted file mode 100644 index 9a496ff..0000000 --- a/pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#define RESTRICT __restrict__ - -#include <cstdint> - - -namespace {{root_namespace}} { - - -} // namespace {{root_namespace}} diff --git a/pystencils_sfg/source_concepts/containers.py b/pystencils_sfg/source_concepts/containers.py deleted file mode 100644 index 7c6f80c..0000000 --- a/pystencils_sfg/source_concepts/containers.py +++ /dev/null @@ -1,20 +0,0 @@ -from abc import ABC, abstractmethod - -from .source_concepts import SrcObject, SrcMemberAccess - -class SrcContiguousContainer(SrcObject): - def __init__(self, src_type, identifier: Optional[str]): - super().__init__(src_type, identifier) - - @abstractmethod - def ptr(self) -> SrcMemberAccess: - pass - - @abstractmethod - def size(self, dimension: int) -> SrcMemberAccess: - pass - - @abstractmethod - def stride(self, dimension: int) -> SrcMemberAccess: - pass - diff --git a/pystencils_sfg/source_concepts/cpp/std_mdspan.py b/pystencils_sfg/source_concepts/cpp/std_mdspan.py deleted file mode 100644 index e92480a..0000000 --- a/pystencils_sfg/source_concepts/cpp/std_mdspan.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Optional - -from ..source_concepts import SrcMemberAccess -from ..containers import SrcContiguousContainer - -class std_mdspan(SrcContiguousContainer): - def __init__(self, identifer: str): - super().__init__("std::mdspan", identifier) - - def ptr(self): - return SrcMemberAccess(self, f"{self._identifier}.data_handle()") - - def size(self, dimension: int): - return SrcMemberAccess(self, f"{self._identifier}.extents().extent({dimension})") - - def stride(self, dimension: int): - return SrcMemberAccess(self, f"{self._identifier}.stride({dimension})") diff --git a/pystencils_sfg/source_concepts/source_concepts.py b/pystencils_sfg/source_concepts/source_concepts.py deleted file mode 100644 index 62463b7..0000000 --- a/pystencils_sfg/source_concepts/source_concepts.py +++ /dev/null @@ -1,27 +0,0 @@ -from typing import Optional -from abc import ABC, abstractmethod -from pystencils import TypedSymbol - -class SrcClass: - def __init__(self): - pass - - -class SrcObject(ABC): - def __init__(self, src_type, identifier: Optional[str]): - self._src_type = src_type - self._identifier = identifier - - @property - def _sfg_symbol(self): - return TypedSymbol(self._identifier, self._src_type) - - -class SrcMemberAccess(): - def __init__(self, obj: SrcObject, code_string: str): - self._obj = obj - self._code_string = code_string - - def _sfg_code_string(): - return self._code_string - diff --git a/pystencils_sfg/tree/__init__.py b/pystencils_sfg/tree/__init__.py deleted file mode 100644 index 9615188..0000000 --- a/pystencils_sfg/tree/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .basic_nodes import SfgCallTreeNode, SfgKernelCallNode, SfgBlock, SfgSequence -from .conditional import SfgBranch, SfgCondition - -__all__ = [ - SfgCallTreeNode, SfgKernelCallNode, SfgSequence, SfgBlock, - SfgCondition, SfgBranch -] \ No newline at end of file diff --git a/pystencils_sfg/__init__.py b/pystencilssfg/__init__.py similarity index 99% rename from pystencils_sfg/__init__.py rename to pystencilssfg/__init__.py index 88406b1..04329f1 100644 --- a/pystencils_sfg/__init__.py +++ b/pystencilssfg/__init__.py @@ -4,4 +4,4 @@ from .kernel_namespace import SfgKernelNamespace, SfgKernelHandle __all__ = [ 'SourceFileGenerator', 'SfgContext', 'SfgKernelNamespace', 'SfgKernelHandle' -] \ No newline at end of file +] diff --git a/pystencils_sfg/context.py b/pystencilssfg/context.py similarity index 65% rename from pystencils_sfg/context.py rename to pystencilssfg/context.py index 7fc0176..572d843 100644 --- a/pystencils_sfg/context.py +++ b/pystencilssfg/context.py @@ -1,16 +1,21 @@ -from typing import Callable, Sequence, Generator, Union, Optional +from typing import Generator, Union, Optional, Sequence from dataclasses import dataclass +import sys import os from os import path +from argparse import ArgumentParser + from jinja2.filters import do_indent +from pystencils import Field from pystencils.astnodes import KernelFunction from .kernel_namespace import SfgKernelNamespace, SfgKernelHandle from .tree import SfgCallTreeNode, SfgSequence, SfgKernelCallNode, SfgCondition, SfgBranch -from .tree.builders import SfgBranchBuilder, SfgSequencer +from .tree.builders import SfgBranchBuilder, make_sequence +from .source_concepts.containers import SrcField from .source_components import SfgFunction @@ -25,23 +30,29 @@ class SfgCodeStyle: class SourceFileGenerator: def __init__(self, namespace: str = "pystencils", - basename: str = None, codestyle: SfgCodeStyle = SfgCodeStyle()): - if basename is None: - import __main__ - scriptpath = __main__.__file__ - scriptname = path.split(scriptpath)[1] - basename = path.splitext(scriptname)[0] + parser = ArgumentParser( + "pystencilssfg", + description="pystencils Source File Generator") + + parser.add_argument("script_args", nargs='*') + parser.add_argument("-d", "--output-dir", type=str, default='.', dest='output_directory') + + args = parser.parse_args(sys.argv) - self.basename = basename - self.header_filename = basename + ".h" - self.cpp_filename = basename + ".cpp" + import __main__ + scriptpath = __main__.__file__ + scriptname = path.split(scriptpath)[1] + basename = path.splitext(scriptname)[0] - self._context = SfgContext(namespace, codestyle) + self._context = SfgContext(args.script_args, namespace, codestyle) + + from .emitters.cpu.basic_cpu import BasicCpuEmitter + self._emitter = BasicCpuEmitter(self._context, basename, args.output_directory) def clean_files(self): - for file in (self.header_filename, self.cpp_filename): + for file in self._emitter.output_files: if path.exists(file): os.remove(file) @@ -51,12 +62,12 @@ class SourceFileGenerator: def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: - from .emitters.cpu.basic_cpu import BasicCpuEmitter - BasicCpuEmitter(self._context, self.basename).write_files() + self._emitter.write_files() class SfgContext: - def __init__(self, root_namespace: str, codestyle: SfgCodeStyle): + def __init__(self, argv, root_namespace: str, codestyle: SfgCodeStyle): + self._argv = argv self._root_namespace = root_namespace self._codestyle = codestyle self._default_kernel_namespace = SfgKernelNamespace(self, "kernels") @@ -66,8 +77,9 @@ class SfgContext: self._kernel_namespaces = { self._default_kernel_namespace.name : self._default_kernel_namespace } self._functions = dict() - # Builder Components - self._sequencer = SfgSequencer(self) + @property + def argv(self) -> Sequence[str]: + return self._argv @property def root_namespace(self) -> str: @@ -88,6 +100,9 @@ class SfgContext: kns = SfgKernelNamespace(self, name) self._kernel_namespaces[name] = kns return kns + + def includes(self) -> Generator[str, None, None]: + yield from self._includes def kernel_namespaces(self) -> Generator[SfgKernelNamespace, None, None]: yield from self._kernel_namespaces.values() @@ -96,7 +111,12 @@ class SfgContext: yield from self._functions.values() def include(self, header_file: str): + if not (header_file.startswith("<") and header_file.endswith(">")): + if not (header_file.startswith('"') and header_file.endswith('"')): + header_file = f'"{header_file}"' + self._includes.append(header_file) + def function(self, name: str, @@ -114,7 +134,7 @@ class SfgContext: raise TypeError(f"Invalid type of argument `ast_or_kernel_handle`!") else: def sequencer(*args: SfgCallTreeNode): - tree = self.seq(*args) + tree = make_sequence(*args) func = SfgFunction(self, name, tree) self._functions[name] = func @@ -125,14 +145,16 @@ class SfgContext: # Call Tree Node Factory #---------------------------------------------------------------------------------------------- - @property - def seq(self) -> SfgSequencer: - return self._sequencer - def call(self, kernel_handle: SfgKernelHandle) -> SfgKernelCallNode: return SfgKernelCallNode(kernel_handle) @property def branch(self) -> SfgBranchBuilder: - return SfgBranchBuilder(self) + return SfgBranchBuilder() + + def map_field(self, field: Field, src_object: Optional[SrcField] = None) -> SfgSequence: + if src_object is None: + raise NotImplementedError("Automatic field extraction is not implemented yet.") + else: + return src_object.extract_parameters(field) \ No newline at end of file diff --git a/pystencils_sfg/emitters/__init__.py b/pystencilssfg/emitters/__init__.py similarity index 100% rename from pystencils_sfg/emitters/__init__.py rename to pystencilssfg/emitters/__init__.py diff --git a/pystencils_sfg/emitters/cpu/__init__.py b/pystencilssfg/emitters/cpu/__init__.py similarity index 100% rename from pystencils_sfg/emitters/cpu/__init__.py rename to pystencilssfg/emitters/cpu/__init__.py diff --git a/pystencils_sfg/emitters/cpu/basic_cpu.py b/pystencilssfg/emitters/cpu/basic_cpu.py similarity index 57% rename from pystencils_sfg/emitters/cpu/basic_cpu.py rename to pystencilssfg/emitters/cpu/basic_cpu.py index 2181681..d36c98e 100644 --- a/pystencils_sfg/emitters/cpu/basic_cpu.py +++ b/pystencilssfg/emitters/cpu/basic_cpu.py @@ -1,26 +1,37 @@ from jinja2 import Environment, PackageLoader, StrictUndefined +from os import path + from ...context import SfgContext class BasicCpuEmitter: - def __init__(self, ctx: SfgContext, basename: str): + def __init__(self, ctx: SfgContext, basename: str, output_directory: str): self._ctx = ctx self._basename = basename + self._output_directory = output_directory self._header_filename = basename + ".h" self._cpp_filename = basename + ".cpp" + @property + def output_files(self) -> str: + return ( + path.join(self._output_directory, self._header_filename), + path.join(self._output_directory, self._cpp_filename) + ) + def write_files(self): jinja_context = { 'ctx': self._ctx, 'basename': self._basename, 'root_namespace': self._ctx.root_namespace, + 'includes': list(self._ctx.includes()), 'kernel_namespaces': list(self._ctx.kernel_namespaces()), 'functions': list(self._ctx.functions()) } template_name = "BasicCpu" - env = Environment(loader=PackageLoader('pystencils_sfg.emitters.cpu'), undefined=StrictUndefined) + env = Environment(loader=PackageLoader('pystencilssfg.emitters.cpu'), undefined=StrictUndefined) from .jinja_filters import add_filters_to_jinja add_filters_to_jinja(env) @@ -28,8 +39,8 @@ class BasicCpuEmitter: header = env.get_template(f"{template_name}.tmpl.h").render(**jinja_context) source = env.get_template(f"{template_name}.tmpl.cpp").render(**jinja_context) - with open(self._header_filename, 'w') as headerfile: + with open(path.join(self._output_directory, self._header_filename), 'w') as headerfile: headerfile.write(header) - with open(self._cpp_filename, 'w') as cppfile: + with open(path.join(self._output_directory, self._cpp_filename), 'w') as cppfile: cppfile.write(source) diff --git a/pystencils_sfg/emitters/cpu/jinja_filters.py b/pystencilssfg/emitters/cpu/jinja_filters.py similarity index 88% rename from pystencils_sfg/emitters/cpu/jinja_filters.py rename to pystencilssfg/emitters/cpu/jinja_filters.py index 0abbdf3..d3602da 100644 --- a/pystencils_sfg/emitters/cpu/jinja_filters.py +++ b/pystencilssfg/emitters/cpu/jinja_filters.py @@ -4,8 +4,7 @@ from pystencils.astnodes import KernelFunction from pystencils import Backend from pystencils.backends import generate_c -from pystencils_sfg.tree import SfgCallTreeNode -from pystencils_sfg.source_components import SfgFunction +from pystencilssfg.source_components import SfgFunction @pass_context def generate_kernel_definition(ctx, ast: KernelFunction): diff --git a/pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.cpp b/pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.cpp similarity index 99% rename from pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.cpp rename to pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.cpp index b9782a3..c5d368a 100644 --- a/pystencils_sfg/emitters/cpu/templates/BasicCpu.tmpl.cpp +++ b/pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.cpp @@ -23,11 +23,9 @@ namespace {{ kns.name }}{ *************************************************************************************/ {% for function in functions %} - void {{ function.name }} ( {{ function | generate_function_parameter_list }} ) { {{ function | generate_function_body | indent(2) }} } - {% endfor %} } // namespace {{root_namespace}} diff --git a/pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.h b/pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.h new file mode 100644 index 0000000..8301a21 --- /dev/null +++ b/pystencilssfg/emitters/cpu/templates/BasicCpu.tmpl.h @@ -0,0 +1,18 @@ +#pragma once + +#define RESTRICT __restrict__ + +#include <cstdint> + +{% for header in includes %} +#include {{header}} +{% endfor %} + + +namespace {{root_namespace}} { + +{% for function in functions %} +void {{ function.name }} ( {{ function | generate_function_parameter_list }} ); +{% endfor %} + +} // namespace {{root_namespace}} diff --git a/pystencils_sfg/kernel_namespace.py b/pystencilssfg/kernel_namespace.py similarity index 98% rename from pystencils_sfg/kernel_namespace.py rename to pystencilssfg/kernel_namespace.py index 8545299..4af5da4 100644 --- a/pystencils_sfg/kernel_namespace.py +++ b/pystencilssfg/kernel_namespace.py @@ -1,4 +1,7 @@ # from .context import SfgContext + +from typing import Sequence + from pystencils import CreateKernelConfig, create_kernel from pystencils.astnodes import KernelFunction diff --git a/pystencils_sfg/source_components.py b/pystencilssfg/source_components.py similarity index 95% rename from pystencils_sfg/source_components.py rename to pystencilssfg/source_components.py index 68e8be3..c296e97 100644 --- a/pystencils_sfg/source_components.py +++ b/pystencilssfg/source_components.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING if TYPE_CHECKING: from .context import SfgContext diff --git a/pystencils_sfg/source_concepts/__init__.py b/pystencilssfg/source_concepts/__init__.py similarity index 100% rename from pystencils_sfg/source_concepts/__init__.py rename to pystencilssfg/source_concepts/__init__.py diff --git a/pystencilssfg/source_concepts/containers.py b/pystencilssfg/source_concepts/containers.py new file mode 100644 index 0000000..c634310 --- /dev/null +++ b/pystencilssfg/source_concepts/containers.py @@ -0,0 +1,36 @@ +from typing import Optional, Union +from abc import ABC, abstractmethod + +from pystencils import Field +from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol + +from .source_concepts import SrcObject +from ..tree import SfgStatements, SfgSequence + +class SrcField(SrcObject): + def __init__(self, src_type, identifier: Optional[str]): + super().__init__(src_type, identifier) + + @abstractmethod + def extract_ptr(self, ptr_symbol: FieldPointerSymbol) -> SfgStatements: + pass + + @abstractmethod + def extract_size(self, coordinate: int, size: Union[int, FieldShapeSymbol]) -> SfgStatements: + pass + + @abstractmethod + def extract_stride(self, coordinate: int, stride: Union[int, FieldStrideSymbol]) -> SfgStatements: + pass + + def extract_parameters(self, field: Field) -> SfgSequence: + ptr = FieldPointerSymbol(field.name, field.dtype, False) + + from ..tree import make_sequence + + return make_sequence( + self.extract_ptr(ptr), + *(self.extract_size(c, s) for c, s in enumerate(field.shape)), + *(self.extract_stride(c, s) for c, s in enumerate(field.strides)) + ) + diff --git a/pystencilssfg/source_concepts/cpp/std_mdspan.py b/pystencilssfg/source_concepts/cpp/std_mdspan.py new file mode 100644 index 0000000..93c8811 --- /dev/null +++ b/pystencilssfg/source_concepts/cpp/std_mdspan.py @@ -0,0 +1,43 @@ +from typing import Union + +from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol + +from ...tree import SfgStatements +from ..containers import SrcField + +class std_mdspan(SrcField): + def __init__(self, identifer: str): + super().__init__("std::mdspan", identifer) + + def extract_ptr(self, ptr_symbol: FieldPointerSymbol): + return SfgStatements( + f"{ptr_symbol.dtype} {ptr_symbol.name} = {self._identifier}.data_handle();", + (ptr_symbol, ), + (self, ) + ) + + def extract_size(self, coordinate: int, size: Union[int, FieldShapeSymbol]) -> SfgStatements: + if isinstance(size, FieldShapeSymbol): + return SfgStatements( + f"{size.dtype} {size.name} = {self._identifier}.extents().extent({coordinate});", + (size, ), + (self, ) + ) + else: + return SfgStatements( + f"assert( {self._identifier}.extents().extent({coordinate}) == {size} );", + (), (self, ) + ) + + def extract_stride(self, coordinate: int, stride: Union[int, FieldShapeSymbol]) -> SfgStatements: + if isinstance(stride, FieldShapeSymbol): + return SfgStatements( + f"{stride.dtype} {stride.name} = {self._identifier}.stride({coordinate});", + (stride, ), + (self, ) + ) + else: + return SfgStatements( + f"assert( {self._identifier}.stride({coordinate}) == {stride} );", + (), (self, ) + ) diff --git a/pystencilssfg/source_concepts/source_concepts.py b/pystencilssfg/source_concepts/source_concepts.py new file mode 100644 index 0000000..4e663f4 --- /dev/null +++ b/pystencilssfg/source_concepts/source_concepts.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import Optional, Sequence, Union, Set +from abc import ABC +from pystencils import TypedSymbol + +class SrcObject(ABC): + def __init__(self, src_type, identifier: Optional[str]): + self._src_type = src_type + self._identifier = identifier + + @property + def src_type(self): + return self._src_type + + @property + def identifier(self): + return self._identifier + + @property + def typed_symbol(self): + return TypedSymbol(self._identifier, self._src_type) + + + + + +# class SrcExpression(SrcStatements): +# """Represents a single expression of the source language, e.g. a C++ expression +# (c.f. https://en.cppreference.com/w/cpp/language/expressions). + +# It is the user's responsibility to ensure that the code string is valid code in the output language, +# and that the list of required objects is complete. + +# Args: +# code_string: Code to be printed out. +# required_objects: Objects (as `SrcObject` or `TypedSymbol`) that are required as input to this expression. +# """ + +# def __init__(self, +# code_string: str, +# required_objects: Sequence[Union[SrcObject, TypedSymbol]]): +# super().__init__(code_string, (), required_objects) diff --git a/pystencilssfg/tree/__init__.py b/pystencilssfg/tree/__init__.py new file mode 100644 index 0000000..44603b7 --- /dev/null +++ b/pystencilssfg/tree/__init__.py @@ -0,0 +1,9 @@ +from .basic_nodes import SfgCallTreeNode, SfgKernelCallNode, SfgBlock, SfgSequence, SfgStatements +from .conditional import SfgBranch, SfgCondition +from .builders import make_sequence + +__all__ = [ + SfgCallTreeNode, SfgKernelCallNode, SfgSequence, SfgBlock, SfgStatements, + SfgCondition, SfgBranch, + make_sequence +] \ No newline at end of file diff --git a/pystencils_sfg/tree/basic_nodes.py b/pystencilssfg/tree/basic_nodes.py similarity index 53% rename from pystencils_sfg/tree/basic_nodes.py rename to pystencilssfg/tree/basic_nodes.py index bff3d00..3750bd5 100644 --- a/pystencils_sfg/tree/basic_nodes.py +++ b/pystencilssfg/tree/basic_nodes.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Sequence +from typing import TYPE_CHECKING, Any, Sequence, Set, Union, Iterable if TYPE_CHECKING: from ..context import SfgContext @@ -10,6 +10,7 @@ from functools import reduce from jinja2.filters import do_indent from ..kernel_namespace import SfgKernelHandle +from ..source_concepts.source_concepts import SrcObject from pystencils.typing import TypedSymbol @@ -38,37 +39,54 @@ class SfgCallTreeLeaf(SfgCallTreeNode, ABC): @property @abstractmethod - def required_symbols(self) -> set(TypedSymbol): + def required_symbols(self) -> Set[TypedSymbol]: pass -class SfgParameterDefinition(SfgCallTreeLeaf): - def __init__(self, defined_param: TypedSymbol, required_params: Set[TypedSymbol], code_string: str): - self._defined_param = defined_param - self._required_params = required_params +class SfgStatements(SfgCallTreeLeaf): + """Represents (a sequence of) statements in the source language. + + This class groups together arbitrary code strings + (e.g. sequences of C++ statements, cf. https://en.cppreference.com/w/cpp/language/statements), + and annotates them with the set of symbols read and written by these statements. + + It is the user's responsibility to ensure that the code string is valid code in the output language, + and that the lists of required and defined objects are correct and complete. + + Args: + code_string: Code to be printed out. + defined_objects: Objects (as `SrcObject` or `TypedSymbol`) that will be newly defined and visible to + code in sequence after these statements. + required_objects: Objects (as `SrcObject` or `TypedSymbol`) that are required as input to these statements. + """ + + def __init__(self, + code_string: str, + defined_objects: Sequence[Union[SrcObject, TypedSymbol]], + required_objects: Sequence[Union[SrcObject, TypedSymbol]]): self._code_string = code_string - - @property - def defined_symbol(self) -> TypedSymbol: - return self._defined_param - + + def to_symbol(obj: Union[SrcObject, TypedSymbol]): + if isinstance(obj, SrcObject): + self._required_symbols.add(obj.typed_symbol) + elif isinstance(obj, TypedSymbol): + self._required_symbols.add(obj) + else: + raise ValueError(f"Required object in expression is neither TypedSymbol nor SrcObject: {obj}") + + self._defined_symbols = set(map(to_symbol, defined_objects)) + self._required_symbols = set(map(to_symbol, required_objects)) + @property - def required_symbols(self) -> set(TypedSymbol): - return self._required_params - - def get_code(self): - return self._code_string - - -class SfgCustomStatement(SfgCallTreeLeaf): - def __init__(self, statement: str): - self._statement = statement - - def required_symbols(self) -> set(TypedSymbol): - return set() + def required_symbols(self) -> Set[TypedSymbol]: + return self._required_symbols + @property + def defined_symbols(self) -> Set[TypedSymbol]: + return self._defined_symbols + def get_code(self, ctx: SfgContext) -> str: - return self._statement + return self._code_string class SfgSequence(SfgCallTreeNode): @@ -103,7 +121,7 @@ class SfgKernelCallNode(SfgCallTreeLeaf): self._kernel_handle = kernel_handle @property - def required_symbols(self) -> set(TypedSymbol): + def required_symbols(self) -> Set[TypedSymbol]: return set(p.symbol for p in self._kernel_handle.parameters) def get_code(self, ctx: SfgContext) -> str: diff --git a/pystencils_sfg/tree/builders.py b/pystencilssfg/tree/builders.py similarity index 54% rename from pystencils_sfg/tree/builders.py rename to pystencilssfg/tree/builders.py index 5f617e3..5c35c56 100644 --- a/pystencils_sfg/tree/builders.py +++ b/pystencilssfg/tree/builders.py @@ -8,41 +8,35 @@ from abc import ABC, abstractmethod from pystencils import Field -from .basic_nodes import SfgCallTreeNode, SfgSequence, SfgBlock, SfgCustomStatement +from .basic_nodes import SfgCallTreeNode, SfgSequence, SfgBlock, SfgStatements from .conditional import SfgCondition, SfgCustomCondition, SfgBranch -from ..source_concepts.containers import SrcContiguousContainer class SfgNodeBuilder(ABC): @abstractmethod def resolve(self) -> SfgCallTreeNode: pass -class SfgSequencer: - def __init__(self, ctx: SfgContext) -> None: - self._ctx = ctx - - def __call__(self, *args) -> SfgSequence: - children = [] - for i, arg in enumerate(args): - if isinstance(arg, SfgNodeBuilder): - children.append(arg.resolve()) - elif isinstance(arg, SfgCallTreeNode): - children.append(arg) - elif isinstance(arg, str): - children.append(SfgCustomStatement(arg)) - elif isinstance(arg, tuple): - # Tuples are treated as blocks - subseq = self(*arg) - children.append(SfgBlock(subseq)) - else: - raise TypeError(f"Sequence argument {i} has invalid type.") - - return SfgSequence(children) +def make_sequence(*args) -> SfgSequence: + children = [] + for i, arg in enumerate(args): + if isinstance(arg, SfgNodeBuilder): + children.append(arg.resolve()) + elif isinstance(arg, SfgCallTreeNode): + children.append(arg) + elif isinstance(arg, str): + children.append(SfgStatements(arg, (), ())) + elif isinstance(arg, tuple): + # Tuples are treated as blocks + subseq = self(*arg) + children.append(SfgBlock(subseq)) + else: + raise TypeError(f"Sequence argument {i} has invalid type.") + + return SfgSequence(children) class SfgBranchBuilder(SfgNodeBuilder): - def __init__(self, ctx: SfgContext): - self._ctx = ctx + def __init__(self): self._phase = 0 self._cond = None @@ -65,9 +59,9 @@ class SfgBranchBuilder(SfgNodeBuilder): self._cond = cond case 1: # Then-branch - self._branch_true = self._ctx.seq(*args) + self._branch_true = make_sequence(*args) case 2: # Else-branch - self._branch_false = self._ctx.seq(*args) + self._branch_false = make_sequence(*args) case _: # There's no third branch! raise TypeError("Branch construct already complete.") @@ -78,14 +72,3 @@ class SfgBranchBuilder(SfgNodeBuilder): def resolve(self) -> SfgCallTreeNode: return SfgBranch(self._cond, self._branch_true, self._branch_false) - -class SfgFieldMappingBuilder(SfgNodeBuilder): - def __init__(self, ctx: SfgContext): - super().__init__(ctx) - - self._field = None - self._container = None - - def __call__(self, field: Field, container: SrcContiguousContainer): - self._field = field - self._container = container \ No newline at end of file diff --git a/pystencils_sfg/tree/conditional.py b/pystencilssfg/tree/conditional.py similarity index 100% rename from pystencils_sfg/tree/conditional.py rename to pystencilssfg/tree/conditional.py diff --git a/pystencils_sfg/tree/visitors.py b/pystencilssfg/tree/visitors.py similarity index 96% rename from pystencils_sfg/tree/visitors.py rename to pystencilssfg/tree/visitors.py index 816abe9..c94ee53 100644 --- a/pystencils_sfg/tree/visitors.py +++ b/pystencilssfg/tree/visitors.py @@ -3,7 +3,7 @@ from functools import reduce from pystencils.typing import TypedSymbol -from .basic_nodes import SfgCallTreeNode, SfgCallTreeLeaf, SfgSequence, SfgParameterDefinition +from .basic_nodes import SfgCallTreeNode, SfgCallTreeLeaf, SfgSequence, SfgStatements class FlattenSequences(): @@ -56,7 +56,7 @@ class ParameterCollector(): params = set() for c in sequence.children[::-1]: - if isinstance(c, SfgParameterDefinitionNode): + if isinstance(c, SfgStatements): params -= c.defined_symbols assert not isinstance(c, SfgSequence), "Sequence not flattened." diff --git a/tests/TestSequencing.py b/tests/TestSequencing.py index 1303bd3..ad9c522 100644 --- a/tests/TestSequencing.py +++ b/tests/TestSequencing.py @@ -1,4 +1,4 @@ -from pystencils_sfg import SourceFileGenerator +from pystencilssfg import SourceFileGenerator from lbmpy.advanced_streaming import Timestep from lbmpy import LBMConfig, create_lb_ast diff --git a/tests/mdspan/Makefile b/tests/mdspan/Makefile new file mode 100644 index 0000000..cf51bde --- /dev/null +++ b/tests/mdspan/Makefile @@ -0,0 +1,34 @@ + +CXX := clang++ +CXX_FLAGS := '-DDEBUG -g' + +PYTHON := python + +MKDIR := mkdir -p +dir_guard = $(MKDIR) $(@D) + +OBJ := obj +BIN := bin +GEN_SRC := generated_src + +.PHONY: all clean + +all: $(BIN)/mdspan_test + +clean: + rm -rf $(BIN) $(OUT) $(GEN_SRC) + +$(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h &: kernels.py + @$(dir_guard) + $(PYTHON) $< -d $(@D) + +$(OBJ)/kernels.o: $(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h + @$(dir_guard) + $(CXX) $(CXX_FLAGS) -c -o $@ $< + +$(OBJ)/main.o: main.cpp $(GEN_SRC)/kernels.h + $(CXX) $(CXX_FLAGS) -c -o $@ $< + +$(BIN)/mdspan_test: $(OBJ)/kernels.o $(OBJ)/main.o + $(CXX) $(CXX_FLAGS) -o $@ $^ + diff --git a/tests/mdspan/kernels.py b/tests/mdspan/kernels.py new file mode 100644 index 0000000..11942a3 --- /dev/null +++ b/tests/mdspan/kernels.py @@ -0,0 +1,8 @@ +from pystencilssfg import SourceFileGenerator + +with SourceFileGenerator() as sfg: + sfg.include("<iostream>") + + sfg.function("myFunction")( + r'std::cout << "mdspans!\n";' + ) diff --git a/tests/mdspan/main.cpp b/tests/mdspan/main.cpp new file mode 100644 index 0000000..f44ca21 --- /dev/null +++ b/tests/mdspan/main.cpp @@ -0,0 +1,6 @@ +#include "generated_src/kernels.h" + +int main(int argc, char ** argv){ + pystencils::myFunction(); + return 0; +} -- GitLab