diff --git a/doc/ref_kernel.rst b/doc/ref_kernel.rst index 3f01b0764f71e9ce2de86a66cc71f56473a7dc9f..07b7836d82596892f1d94e336dfa81e1b5a7a881 100644 --- a/doc/ref_kernel.rst +++ b/doc/ref_kernel.rst @@ -327,15 +327,25 @@ Expressions Loopy's expressions are a slight superset of the expressions supported by :mod:`pymbolic`. -* ``if`` -* ``elif`` (following an ``if``) -* ``else`` (following an ``if`` / ``elif``) +* ``if(cond, then, else_)`` + +* ``a[[ 8*i + j ]]``: Linear subscripts. + See :class:`loopy.symbolic.LinearSubscript`. + * ``reductions`` - * duplication of reduction inames + See :class:`loopy.symbolic.Reduction`. + * ``reduce`` vs ``simul_reduce`` + * complex-valued arithmetic + * tagging of array access and substitution rule use ("$") + See :class:`loopy.symbolic.TaggedVariable`. + * ``indexof``, ``indexof_vec`` +* ``cast(type, value)``: No parse syntax currently. + See :class:`loopy.symbolic.TypeCast`. + TODO: Functions TODO: Reductions diff --git a/loopy/__init__.py b/loopy/__init__.py index e48a8b3bdb77f2e2e10bb3546fafe7bf3ceb66c4..7a853d11570226a7a3fe35539f590e7f78ea3f44 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -27,7 +27,7 @@ import six from six.moves import range, zip from loopy.symbolic import ( - TaggedVariable, Reduction, LinearSubscript, ) + TaggedVariable, Reduction, LinearSubscript, TypeCast) from loopy.diagnostic import LoopyError, LoopyWarning @@ -145,7 +145,7 @@ from loopy.target.numba import NumbaTarget, NumbaCudaTarget __all__ = [ - "TaggedVariable", "Reduction", "LinearSubscript", + "TaggedVariable", "Reduction", "LinearSubscript", "TypeCast", "auto", diff --git a/loopy/symbolic.py b/loopy/symbolic.py index 543c2743bb98b09b706c2fdbf9188ed0a85d97f2..2d31c63ef13774599de27ae871be64bc5acb7514 100644 --- a/loopy/symbolic.py +++ b/loopy/symbolic.py @@ -104,7 +104,9 @@ class IdentityMapperMixin(object): return expr def map_type_annotation(self, expr, *args): - return TypeAnnotation(expr.type, self.rec(expr.child)) + return type(expr)(expr.type, self.rec(expr.child)) + + map_type_cast = map_type_annotation map_linear_subscript = IdentityMapperBase.map_subscript @@ -147,6 +149,11 @@ class WalkMapper(WalkMapperBase): self.rec(expr.expr, *args) + def map_type_cast(self, expr, *args): + if not self.visit(expr): + return + self.rec(expr.child, *args) + map_tagged_variable = WalkMapperBase.map_variable def map_loopy_function_identifier(self, expr, *args): @@ -219,6 +226,10 @@ class StringifyMapper(StringifyMapperBase): def map_rule_argument(self, expr, enclosing_prec): return "" % expr.index + def map_type_cast(self, expr, enclosing_prec): + from pymbolic.mapper.stringifier import PREC_NONE + return "cast(%s, %s)" % (repr(expr.type), self.rec(expr.child, PREC_NONE)) + class UnidirectionalUnifier(UnidirectionalUnifierBase): def map_reduction(self, expr, other, unis): @@ -273,6 +284,9 @@ class DependencyMapper(DependencyMapperBase): map_linear_subscript = DependencyMapperBase.map_subscript + def map_type_cast(self, expr): + return self.rec(expr.child) + class SubstitutionRuleExpander(IdentityMapper): def __init__(self, rules): @@ -398,6 +412,10 @@ class TypedCSE(p.CommonSubexpression): class TypeAnnotation(p.Expression): + """Undocumented for now. Currently only used internally around LHSs of + assignments that create temporaries. + """ + def __init__(self, type, child): super(TypeAnnotation, self).__init__() self.type = type @@ -406,9 +424,55 @@ class TypeAnnotation(p.Expression): def __getinitargs__(self): return (self.type, self.child) + def stringifier(self): + return StringifyMapper + mapper_method = intern("map_type_annotation") +class TypeCast(p.Expression): + """Only defined for numerical types with semantics matching + :meth:`numpy.ndarray.astype`. + + .. attribute:: child + + The expression to be cast. + """ + + def __init__(self, type, child): + super(TypeCast, self).__init__() + + from loopy.types import to_loopy_type, NumpyType + type = to_loopy_type(type) + + if (not isinstance(type, NumpyType) + or not issubclass(type.dtype.type, np.number)): + from loopy.diagnostic import LoopyError + raise LoopyError("TypeCast only supports numerical numpy types, " + "not '%s'" % type) + + # We're storing the type as a name for now to avoid + # numpy pickling bug madness. (see loopy.types) + self._type_name = type.dtype.name + self.child = child + + @property + def type(self): + from loopy.types import NumpyType + return NumpyType(np.dtype(self._type_name)) + + # init_arg_names is a misnomer--they're attribute names used for pickling. + init_arg_names = ("_type_name", "child") + + def __getinitargs__(self): + return (self._type_name, self.child) + + def stringifier(self): + return StringifyMapper + + mapper_method = intern("map_type_cast") + + class TaggedVariable(p.Variable): """This is an identifier with a tag, such as 'matrix$one', where 'one' identifies this specific use of the identifier. This mechanism @@ -1562,6 +1626,9 @@ class BatchedAccessRangeMapper(WalkMapper): def map_reduction(self, expr, inames): return WalkMapper.map_reduction(self, expr, inames | set(expr.inames)) + def map_type_cast(self, expr, inames): + return self.rec(expr.child, inames) + class AccessRangeMapper(object): diff --git a/loopy/target/c/codegen/expression.py b/loopy/target/c/codegen/expression.py index 5d269a8bcbfb07405f76a5ac1821500b87212c2a..caee73eb1c3320f03ceac66e55e8f5c0bfadbbc2 100644 --- a/loopy/target/c/codegen/expression.py +++ b/loopy/target/c/codegen/expression.py @@ -340,6 +340,11 @@ class ExpressionToCExpressionMapper(IdentityMapper): expr.operator, self.rec(expr.right, inner_type_context)) + def map_type_cast(self, expr, type_context): + registry = self.codegen_state.ast_builder.target.get_dtype_registry() + cast = var("(%s)" % registry.dtype_to_ctype(expr.type)) + return cast(self.rec(expr.child, type_context)) + def map_constant(self, expr, type_context): if isinstance(expr, (complex, np.complexfloating)): try: diff --git a/loopy/type_inference.py b/loopy/type_inference.py index 409cbbc5ebd5feb13b04eeba1671f639663bfcf1..6ffc1dff5220ab48c6c87ec29fec6e44d57ba133 100644 --- a/loopy/type_inference.py +++ b/loopy/type_inference.py @@ -237,6 +237,12 @@ class TypeInferenceMapper(CombineMapper): else: raise TypeInferenceFailure("Cannot deduce type of constant '%s'" % expr) + def map_type_cast(self, expr): + subtype, = self.rec(expr.child) + if not issubclass(subtype.dtype.type, np.number): + raise LoopyError("Can't cast a '%s' to '%s'" % (subtype, expr.type)) + return [expr.type] + def map_subscript(self, expr): return self.rec(expr.aggregate) diff --git a/test/test_numa_diff.py b/test/test_numa_diff.py index 0de08f5f616937604bc2c93581c5a8a1770164f4..eff3dbd0e07439bbec399479183a7e9ddb69b9ff 100644 --- a/test/test_numa_diff.py +++ b/test/test_numa_diff.py @@ -28,6 +28,7 @@ import pytest import loopy as lp import pyopencl as cl import sys +import os pytestmark = pytest.mark.importorskip("fparser") @@ -49,7 +50,7 @@ __all__ = [ def test_gnuma_horiz_kernel(ctx_factory, ilp_multiple, Nq, opt_level): # noqa ctx = ctx_factory() - filename = "strongVolumeKernels.f90" + filename = os.path.join(os.path.dirname(__file__), "strongVolumeKernels.f90") with open(filename, "r") as sourcef: source = sourcef.read() diff --git a/test/test_target.py b/test/test_target.py index ad0cb7439bfdd6200e020c0becadcd73072ceef4..01a2e5d9d29622b9410576e0f30510d0b5610b1c 100644 --- a/test/test_target.py +++ b/test/test_target.py @@ -240,6 +240,44 @@ def test_numba_cuda_target(): print(lp.generate_code_v2(knl).all_code()) +def test_sized_integer_c_codegen(ctx_factory): + ctx = ctx_factory() + queue = cl.CommandQueue(ctx) + + from pymbolic import var + knl = lp.make_kernel( + "{[i]: 0<=i ctr = make_uint2(0, 0)", + lp.Assignment("a[i]", lp.TypeCast(np.int64, var("ctr")) << var("i"))] + ) + + with pytest.raises(lp.LoopyError): + knl = lp.preprocess_kernel(knl) + + +def test_target_invalid_type_cast(): + dtype = np.dtype([('', ' 1: exec(sys.argv[1])