diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 69f89548618e86b408a31af240bee84678c859c1..7196dad863474d9b6ea9df9d9d0ae90b3e14986d 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -111,9 +111,9 @@ always see loopy's view of a kernel by printing it. KERNEL: loopy_kernel --------------------------------------------------------------------------- ARGUMENTS: - a: GlobalArg, type: , shape: (n), dim_tags: (N0:stride:1) - n: ValueArg, type: - out: GlobalArg, type: , shape: (n), dim_tags: (N0:stride:1) + a: GlobalArg, type: , shape: (n), dim_tags: (N0:stride:1) + n: ValueArg, type: + out: GlobalArg, type: , shape: (n), dim_tags: (N0:stride:1) --------------------------------------------------------------------------- DOMAINS: [n] -> { [i] : 0 <= i < n } @@ -154,7 +154,7 @@ following: See :ref:`specifying-arguments`. * Loopy has not determined the type of ``a`` and ``out``. The data type is - given as ````, which means that these types will be determined + given as ````, which means that these types will be determined by the data passed in when the kernel is invoked. Loopy generates (and caches!) a copy of the kernel for each combination of types passed in. diff --git a/loopy/kernel/array.py b/loopy/kernel/array.py index 5d4240b9ab3e1ce2ad356a93b5e21b3bbf4d499e..34f58e2864b51db9ffa1f2c0657d8fc4e406931f 100644 --- a/loopy/kernel/array.py +++ b/loopy/kernel/array.py @@ -549,15 +549,55 @@ class ArrayBase(ImmutableRecord): .. attribute :: name .. attribute :: dtype + the :class:`loopy.loopytype` of the array. + if this is *none*, :mod:`loopy` will try to continue without + knowing the type of this array, where the idea is that precise + knowledge of the type will become available at invocation time. + :class:`loopy.compiledkernel` (and thereby + :meth:`loopy.loopkernel.__call__`) automatically add this type + information based on invocation arguments. + + note that some transformations, such as :func:`loopy.add_padding` + cannot be performed without knowledge of the exact *dtype*. .. attribute :: shape + May be one of the following: + + * *None*. In this case, no shape is intended to be specified, + only the strides will be used to access the array. Bounds checking + will not be performed. + + * :class:`loopy.auto`. The shape will be determined by finding the + access footprint. + + * a tuple like like :attr:`numpy.ndarray.shape`. + + Each entry of the tuple is also allowed to be a :mod:`pymbolic` + expression involving kernel parameters, or a (potentially-comma + separated) or a string that can be parsed to such an expression. + + Any element of the shape tuple not used to compute strides + may be *None*. + .. attribute:: dim_tags See :ref:`data-dim-tags`. .. attribute:: offset + Offset from the beginning of the buffer to the point from + which the strides are counted. May be one of + + * 0 or None + * a string (that is interpreted as an argument name). + * a pymbolic expression + * :class:`loopy.auto`, in which case an offset argument + is added automatically, immediately following this argument. + :class:`loopy.CompiledKernel` is even smarter in its treatment of + this case and will compile custom versions of the kernel based on + whether the passed arrays have offsets or not. + .. attribute:: dim_names A tuple of strings providing names for the array axes, or *None*. @@ -590,40 +630,12 @@ class ArrayBase(ImmutableRecord): All of the following (except *name*) are optional. Specify either strides or shape. - :arg name: May contain multiple names separated by - commas, in which case multiple arguments, - each with identical properties, are created - for each name. - :arg dtype: the :class:`numpy.dtype` of the array. - If this is *None*, :mod:`loopy` will try to continue without - knowing the type of this array, where the idea is that precise - knowledge of the type will become available at invocation time. - :class:`loopy.CompiledKernel` (and thereby - :meth:`loopy.LoopKernel.__call__`) automatically add this type - information based on invocation arguments. - - Note that some transformations, such as :func:`loopy.add_padding` - cannot be performed without knowledge of the exact *dtype*. - - :arg shape: May be one of the following: + :arg name: When passed to :class:`loopy.make_kernel`, this may contain + multiple names separated by commas, in which case multiple arguments, + each with identical properties, are created for each name. - * *None*. In this case, no shape is intended to be specified, - only the strides will be used to access the array. Bounds checking - will not be performed. - - * :class:`loopy.auto`. The shape will be determined by finding the - access footprint. - - * a tuple like like :attr:`numpy.ndarray.shape`. - - Each entry of the tuple is also allowed to be a :mod:`pymbolic` - expression involving kernel parameters, or a (potentially-comma - separated) or a string that can be parsed to such an expression. - - Any element of the shape tuple not used to compute strides - may be *None*. - - * A string which can be parsed into the previous form. + :arg shape: May be any of the things specified under :attr:`shape`, + or a string which can be parsed into the previous form. :arg dim_tags: A comma-separated list of tags as understood by :func:`parse_array_dim_tag`. @@ -649,17 +661,8 @@ class ArrayBase(ImmutableRecord): :arg for_atomic: Whether the array is declared for atomic access, and, if necessary, using atomic-capable data types. - :arg offset: Offset from the beginning of the buffer to the point from - which the strides are counted. May be one of + :arg offset: (See :attr:`offset`) - * 0 or None - * a string (that is interpreted as an argument name). - * a pymbolic expression - * :class:`loopy.auto`, in which case an offset argument - is added automatically, immediately following this argument. - :class:`loopy.CompiledKernel` is even smarter in its treatment of - this case and will compile custom versions of the kernel based on - whether the passed arrays have offsets or not. """ for kwarg_name in kwargs: @@ -672,6 +675,14 @@ class ArrayBase(ImmutableRecord): dtype = to_loopy_type(dtype, allow_auto=True, allow_none=True, for_atomic=for_atomic, target=target) + if dtype is lp.auto: + from warnings import warn + warn("Argument/temporary data type should be None if unspecified, " + "not auto. This usage will be disallowed in 2018.", + DeprecationWarning, stacklevel=2) + + dtype = None + strides_known = strides is not None and strides is not lp.auto shape_known = shape is not None and shape is not lp.auto @@ -832,10 +843,10 @@ class ArrayBase(ImmutableRecord): if include_typename: info_entries.append(type(self).__name__) - if self.dtype is lp.auto: - type_str = "" - elif self.dtype is None: - type_str = "" + assert self.dtype is not lp.auto + + if self.dtype is None: + type_str = "" else: type_str = str(self.dtype) diff --git a/loopy/kernel/data.py b/loopy/kernel/data.py index a4e6036cbac0235590d7cc66a201c47ac87d6030..63f5ce7113143f70b82713ccbb974e71b617a879 100644 --- a/loopy/kernel/data.py +++ b/loopy/kernel/data.py @@ -219,9 +219,20 @@ class KernelArgument(ImmutableRecord): dtype = kwargs.pop("dtype", None) from loopy.types import to_loopy_type - kwargs["dtype"] = to_loopy_type( + dtype = to_loopy_type( dtype, allow_auto=True, allow_none=True, target=target) + import loopy as lp + if dtype is lp.auto: + from warnings import warn + warn("Argument/temporary data type should be None if unspecified, " + "not auto. This usage will be disallowed in 2018.", + DeprecationWarning, stacklevel=2) + + dtype = None + + kwargs["dtype"] = dtype + ImmutableRecord.__init__(self, **kwargs) @@ -268,10 +279,10 @@ class ValueArg(KernelArgument): def __str__(self): import loopy as lp - if self.dtype is lp.auto: - type_str = "" - elif self.dtype is None: - type_str = "" + assert self.dtype is not lp.auto + + if self.dtype is None: + type_str = "" else: type_str = str(self.dtype) diff --git a/loopy/preprocess.py b/loopy/preprocess.py index f2b5e7a87022e01bd51368cc3ef3cc60d507d958..ad119e94e74b294e16cdc15c5ab1f723cf7f254b 100644 --- a/loopy/preprocess.py +++ b/loopy/preprocess.py @@ -797,11 +797,10 @@ def _hackily_ensure_multi_assignment_return_values_are_scoped_private(kernel): newly_added_assignments_ids.add(new_assignment_id) - import loopy as lp new_temporaries[new_assignee_name] = ( TemporaryVariable( name=new_assignee_name, - dtype=lp.auto, + dtype=None, scope=temp_var_scope.PRIVATE)) from pymbolic import var @@ -987,7 +986,7 @@ def realize_reduction(kernel, insn_id_filter=None, unknown_types_ok=True, new_temporary_variables[name] = TemporaryVariable( name=name, shape=(), - dtype=lp.auto, + dtype=None, scope=temp_var_scope.PRIVATE) from pymbolic import var diff --git a/loopy/type_inference.py b/loopy/type_inference.py index 0f58ae64e1789ebe9b9b5ca37a3f7dae534e0a2a..fcf8f965b68fd258b0c0f1eae94ec84a39a5b7ee 100644 --- a/loopy/type_inference.py +++ b/loopy/type_inference.py @@ -312,15 +312,8 @@ class TypeInferenceMapper(CombineMapper): from loopy.kernel.data import TemporaryVariable, KernelArgument import loopy as lp - if isinstance(obj, TemporaryVariable): - result = [obj.dtype] - if result[0] is lp.auto: - self.symbols_with_unknown_types.add(expr.name) - return [] - else: - return result - - elif isinstance(obj, KernelArgument): + if isinstance(obj, (KernelArgument, TemporaryVariable)): + assert obj.dtype is not lp.auto result = [obj.dtype] if result[0] is None: self.symbols_with_unknown_types.add(expr.name) @@ -515,10 +508,12 @@ def infer_unknown_types(kernel, expect_completion=False): import loopy as lp for tv in six.itervalues(kernel.temporary_variables): - if tv.dtype is lp.auto: + assert tv.dtype is not lp.auto + if tv.dtype is None: names_for_type_inference.append(tv.name) for arg in kernel.args: + assert arg.dtype is not lp.auto if arg.dtype is None: names_for_type_inference.append(arg.name) diff --git a/loopy/types.py b/loopy/types.py index f095d1d58f9eaebb7dcc9c8d41afa73951f2ba84..8f0f310c305b3d5b24bd6e771b501bb6d9c69224 100644 --- a/loopy/types.py +++ b/loopy/types.py @@ -177,13 +177,20 @@ class AtomicNumpyType(NumpyType, AtomicType): # }}} -def to_loopy_type(dtype, allow_none=False, allow_auto=False, for_atomic=False, +def to_loopy_type(dtype, allow_auto=False, allow_none=False, for_atomic=False, target=None): from loopy.kernel.data import auto - if allow_none and dtype is None: - return dtype - elif allow_auto and dtype is auto: - return dtype + if dtype is None: + if allow_none: + return None + else: + raise LoopyError("dtype may not be none") + + elif dtype is auto: + if allow_auto: + return dtype + else: + raise LoopyError("dtype may not be auto") numpy_dtype = None