diff --git a/assignment_collection/nestedscopes.py b/assignment_collection/nestedscopes.py new file mode 100644 index 0000000000000000000000000000000000000000..dd33eb1a0bfbbec8ff20c29335ba2e9e02592518 --- /dev/null +++ b/assignment_collection/nestedscopes.py @@ -0,0 +1,52 @@ +class NestedScopes: + """Symbol visibility model using nested scopes + + - every accessed symbol that was not defined before, is added as a "free parameter" + - free parameters are global, i.e. they are not in scopes + - push/pop adds or removes a scope + + >>> s = NestedScopes() + >>> s.access_symbol("a") + >>> s.is_defined("a") + False + >>> s.free_parameters + {'a'} + >>> s.define_symbol("b") + >>> s.is_defined("b") + True + >>> s.push() + >>> s.is_defined_locally("b") + False + >>> s.define_symbol("c") + >>> s.pop() + >>> s.is_defined("c") + False + """ + + def __init__(self): + self.free_parameters = set() + self._defined = [set()] + + def access_symbol(self, symbol): + if not self.is_defined(symbol): + self.free_parameters.add(symbol) + + def define_symbol(self, symbol): + self._defined[-1].add(symbol) + + def is_defined(self, symbol): + return any(symbol in scopes for scopes in self._defined) + + def is_defined_locally(self, symbol): + return symbol in self._defined[-1] + + def push(self): + self._defined.append(set()) + + def pop(self): + self._defined.pop() + assert self.depth >= 1 + + @property + def depth(self): + return len(self._defined) diff --git a/kernelcreation.py b/kernelcreation.py index c5f7164e6821a416301d3701d352b61cfa3df297..f4854a6036af27798145b8de3387551bb6ad4235 100644 --- a/kernelcreation.py +++ b/kernelcreation.py @@ -30,7 +30,7 @@ def create_kernel(assignments, target='cpu', data_type="double", iteration_slice cpu_openmp: True or number of threads for OpenMP parallelization, False for no OpenMP cpu_vectorize_info: a dictionary with keys, 'vector_instruction_set', 'assume_aligned' and 'nontemporal' for documentation of these parameters see vectorize function. Example: - '{'vector_instruction_set': 'avx512', 'assume_aligned': True, 'nontemporal':True}' + '{'instruction_set': 'avx512', 'assume_aligned': True, 'nontemporal':True}' gpu_indexing: either 'block' or 'line' , or custom indexing class, see `AbstractIndexing` gpu_indexing_params: dict with indexing parameters (constructor parameters of indexing class) e.g. for 'block' one can specify '{'block_size': (20, 20, 10) }' @@ -171,7 +171,7 @@ def create_staggered_kernel(staggered_field, expressions, subexpressions=(), tar where e.g. ``f[0,0](0)`` is interpreted as value at the left cell boundary, ``f[1,0](0)`` the right cell boundary and ``f[0,0](1)`` the southern cell boundary etc. expressions: sequence of expressions of length dim, defining how the east, southern, (bottom) cell boundary - should be update. + should be updated. subexpressions: optional sequence of Assignments, that define subexpressions used in the main expressions target: 'cpu' or 'gpu' kwargs: passed directly to create_kernel, iteration slice and ghost_layers parameters are not allowed diff --git a/transformations.py b/transformations.py index d3659807ccf300a052b90a6343ccf669ab51d94e..64efc0788073f5ceac96d2600f858205e679c87a 100644 --- a/transformations.py +++ b/transformations.py @@ -7,6 +7,7 @@ import sympy as sp from sympy.logic.boolalg import Boolean from sympy.tensor import IndexedBase from pystencils.assignment import Assignment +from pystencils.assignment_collection.nestedscopes import NestedScopes from pystencils.field import Field, FieldType from pystencils.data_types import TypedSymbol, PointerType, StructType, get_base_type, cast_func, \ pointer_arithmetic_func, get_type_of_expression, collate_types, create_type @@ -727,9 +728,8 @@ class KernelConstraintsCheck: def __init__(self, type_for_symbol, check_independence_condition): self._type_for_symbol = type_for_symbol - self._defined_pure_symbols = set() - self._accessed_pure_symbols = set() + self.scopes = NestedScopes() self._field_writes = defaultdict(set) self.fields_read = set() self.check_independence_condition = check_independence_condition @@ -784,11 +784,11 @@ class KernelConstraintsCheck: if len(self._field_writes[fai]) > 1: raise ValueError("Field {} is written at two different locations".format(lhs.field.name)) elif isinstance(lhs, sp.Symbol): - if lhs in self._defined_pure_symbols: + if self.scopes.is_defined_locally(lhs): raise ValueError("Assignments not in SSA form, multiple assignments to {}".format(lhs.name)) - if lhs in self._accessed_pure_symbols: + if lhs in self.scopes.free_parameters: raise ValueError("Symbol {} is written, after it has been read".format(lhs.name)) - self._defined_pure_symbols.add(lhs) + self.scopes.define_symbol(lhs) def _update_accesses_rhs(self, rhs): if isinstance(rhs, Field.Access) and self.check_independence_condition: @@ -800,7 +800,7 @@ class KernelConstraintsCheck: "{} is read at {} and written at {}".format(rhs.field, rhs.offsets, write_offset)) self.fields_read.add(rhs.field) elif isinstance(rhs, sp.Symbol): - self._accessed_pure_symbols.add(rhs) + self.scopes.access_symbol(rhs) def add_types(eqs, type_for_symbol, check_independence_condition): @@ -829,11 +829,17 @@ def add_types(eqs, type_for_symbol, check_independence_condition): if isinstance(obj, sp.Eq) or isinstance(obj, ast.SympyAssignment) or isinstance(obj, Assignment): return check.process_assignment(obj) elif isinstance(obj, ast.Conditional): + check.scopes.push() false_block = None if obj.false_block is None else visit(obj.false_block) - return ast.Conditional(check.process_expression(obj.condition_expr, type_constants=False), - true_block=visit(obj.true_block), false_block=false_block) + result = ast.Conditional(check.process_expression(obj.condition_expr, type_constants=False), + true_block=visit(obj.true_block), false_block=false_block) + check.scopes.pop() + return result elif isinstance(obj, ast.Block): - return ast.Block([visit(e) for e in obj.args]) + check.scopes.push() + result = ast.Block([visit(e) for e in obj.args]) + check.scopes.pop() + return result elif isinstance(obj, ast.Node) and not isinstance(obj, ast.LoopOverCoordinate): return obj else: