From 890902a95960c0cbfcde545c6415db512b438073 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 2 Jun 2017 12:43:22 -0400 Subject: [PATCH 1/6] Improve error message on lookup access to aggregate type --- loopy/type_inference.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/loopy/type_inference.py b/loopy/type_inference.py index b8b0cbcbf..4df619fea 100644 --- a/loopy/type_inference.py +++ b/loopy/type_inference.py @@ -332,8 +332,13 @@ class TypeInferenceMapper(CombineMapper): if not agg_result: return agg_result - field = agg_result[0].numpy_dtype.fields[expr.name] - dtype = field[0] + fields = agg_result[0].numpy_dtype.fields + if fields is None: + raise LoopyError("cannot look up attribute '%s' in " + "non-aggregate expression '%s'" + % (expr.aggregate, expr.name)) + + dtype = fields[0] return [NumpyType(dtype)] def map_comparison(self, expr): -- GitLab From a55e5138fc0d2ad3e22d18ac6f27c39e3b96b2aa Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 2 Jun 2017 12:44:12 -0400 Subject: [PATCH 2/6] Fix detection of branch in domain tree --- loopy/kernel/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index e8c846fbc..e5305b703 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -615,8 +615,8 @@ class LoopKernel(ImmutableRecordWithoutPickling): # nothin' new continue - domain_parents = [home_domain_index] + ppd[home_domain_index] - current_root = domain_parents[-1] + domain_path_to_root = [home_domain_index] + ppd[home_domain_index] + current_root = domain_path_to_root[-1] previous_leaf = root_to_leaf.get(current_root) if previous_leaf is not None: @@ -626,8 +626,8 @@ class LoopKernel(ImmutableRecordWithoutPickling): # it can introduce artificial restrictions on variables # further up the tree. - prev_parents = set(ppd[previous_leaf]) - if not prev_parents <= set(domain_parents): + prev_path_to_root = set([previous_leaf] + ppd[previous_leaf]) + if not prev_path_to_root <= set(domain_path_to_root): raise CannotBranchDomainTree("iname set '%s' requires " "branch in domain tree (when adding '%s')" % (", ".join(inames), iname)) @@ -636,7 +636,7 @@ class LoopKernel(ImmutableRecordWithoutPickling): pass root_to_leaf[current_root] = home_domain_index - domain_indices.update(domain_parents) + domain_indices.update(domain_path_to_root) return list(root_to_leaf.values()) -- GitLab From 68a3611d3b7debc510a0684bffaa687b12435781 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 2 Jun 2017 12:58:24 -0400 Subject: [PATCH 3/6] Better docs for loop domain forest --- doc/ref_kernel.rst | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/doc/ref_kernel.rst b/doc/ref_kernel.rst index 867443408..9138d9a41 100644 --- a/doc/ref_kernel.rst +++ b/doc/ref_kernel.rst @@ -5,8 +5,8 @@ Reference: Loopy's Model of a Kernel .. _domain-tree: -Loop Domain Tree ----------------- +Loop Domain Forest +------------------ .. {{{ @@ -29,10 +29,29 @@ Note that *n* in the example is not an iname. It is a :ref:`domain-parameters` that is passed to the kernel by the user. To accommodate some data-dependent control flow, there is not actually -a single loop domain, but rather a *tree of loop domains*, -allowing more deeply nested domains to depend on inames +a single loop domain, but rather a *forest of loop domains* (a collection +of trees) allowing more deeply nested domains to depend on inames introduced by domains closer to the root. +Here is an example:: + + { [l] : 0 <= l <= 2 } + { [i] : start <= i < end } + { [j] : start <= j < end } + +The i and j domains are "children" of the l domain (visible from indentation). +This is also how :mod:`loopy` prints the domain forest, to make the parent/child +relationship visible. In the example, the parameters start/end might be read +inside of the 'l' loop. + +The idea is that domains form a forest (a collection of trees), and a +"sub-forest" is extracted that covers all the inames for each +instruction. Each individual sub-tree is then checked for branching, +which is ill-formed. It is declared ill-formed because intersecting, in +the above case, the l, i, and j domains could result in restrictions from the +i domain affecting the j domain by way of how i affects l--which would +be counterintuitive to say the least.) + .. _inames: Inames -- GitLab From ebd1bf99b0cf0c3eebe65e729e975c774ab52be0 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 2 Jun 2017 13:10:35 -0400 Subject: [PATCH 4/6] Re-improve: type inf error messages for invalid aggregate lookups --- loopy/type_inference.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/loopy/type_inference.py b/loopy/type_inference.py index 4df619fea..78d817ce7 100644 --- a/loopy/type_inference.py +++ b/loopy/type_inference.py @@ -332,13 +332,21 @@ class TypeInferenceMapper(CombineMapper): if not agg_result: return agg_result - fields = agg_result[0].numpy_dtype.fields + numpy_dtype = agg_result[0].numpy_dtype + fields = numpy_dtype.fields if fields is None: raise LoopyError("cannot look up attribute '%s' in " "non-aggregate expression '%s'" % (expr.aggregate, expr.name)) - dtype = fields[0] + try: + field = fields[expr.name] + except KeyError: + raise LoopyError("cannot look up attribute '%s' in " + "aggregate expression '%s' of dtype '%s'" + % (expr.aggregate, expr.name, numpy_dtype)) + + dtype = field[0] return [NumpyType(dtype)] def map_comparison(self, expr): -- GitLab From 58949541fe9b24824aa96a35a3eb102dc1b59db6 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 2 Jun 2017 13:11:00 -0400 Subject: [PATCH 5/6] Track pyopencl vec -> cltypes switch --- loopy/target/pyopencl.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/loopy/target/pyopencl.py b/loopy/target/pyopencl.py index 467bc8ee8..00076ced1 100644 --- a/loopy/target/pyopencl.py +++ b/loopy/target/pyopencl.py @@ -363,14 +363,14 @@ class PyOpenCLTarget(OpenCLTarget): raise NotImplementedError("atomics flavor: %s" % self.atomics_flavor) def is_vector_dtype(self, dtype): - from pyopencl.array import vec + import pyopencl.cltypes as cltypes return (isinstance(dtype, NumpyType) - and dtype.numpy_dtype in list(vec.types.values())) + and dtype.numpy_dtype in list(cltypes.vec_types.values())) def vector_dtype(self, base, count): - from pyopencl.array import vec + import pyopencl.cltypes as cltypes return NumpyType( - vec.types[base.numpy_dtype, count], + cltypes.vec_types[base.numpy_dtype, count], target=self) def alignment_requirement(self, type_decl): -- GitLab From 1b1b37b230e9b7ff44babc8f1a1a4ba98a6a3797 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 2 Jun 2017 14:41:47 -0400 Subject: [PATCH 6/6] Add a fallback for unavailable cltypes --- loopy/target/pyopencl.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/loopy/target/pyopencl.py b/loopy/target/pyopencl.py index 00076ced1..8f371085e 100644 --- a/loopy/target/pyopencl.py +++ b/loopy/target/pyopencl.py @@ -363,14 +363,26 @@ class PyOpenCLTarget(OpenCLTarget): raise NotImplementedError("atomics flavor: %s" % self.atomics_flavor) def is_vector_dtype(self, dtype): - import pyopencl.cltypes as cltypes + try: + import pyopencl.cltypes as cltypes + vec_types = cltypes.vec_types + except ImportError: + from pyopencl.array import vec + vec_types = vec.types + return (isinstance(dtype, NumpyType) - and dtype.numpy_dtype in list(cltypes.vec_types.values())) + and dtype.numpy_dtype in list(vec_types.values())) def vector_dtype(self, base, count): - import pyopencl.cltypes as cltypes + try: + import pyopencl.cltypes as cltypes + vec_types = cltypes.vec_types + except ImportError: + from pyopencl.array import vec + vec_types = vec.types + return NumpyType( - cltypes.vec_types[base.numpy_dtype, count], + vec_types[base.numpy_dtype, count], target=self) def alignment_requirement(self, type_decl): -- GitLab