diff --git a/.test-conda-env-py3.yml b/.test-conda-env-py3.yml index 0688c79603a66aabd0e021855e543f751cd76542..2570718a65a4d924683527d8442a812fdea362ed 100644 --- a/.test-conda-env-py3.yml +++ b/.test-conda-env-py3.yml @@ -13,6 +13,6 @@ dependencies: - islpy # Only needed to make pylint succeed -- matplotlib +- matplotlib-base - ipykernel - ply diff --git a/loopy/check.py b/loopy/check.py index 552dbaa369ccadeda52572dfe048ca7b95ca48ea..c176b89b588da6696785d9d6b4bae8fc8639c795 100644 --- a/loopy/check.py +++ b/loopy/check.py @@ -25,7 +25,7 @@ from islpy import dim_type import islpy as isl from loopy.symbolic import WalkMapper, CombineMapper, ResolvedFunction from loopy.diagnostic import (LoopyError, WriteRaceConditionWarning, - warn_with_kernel) + warn_with_kernel, LoopyIndexError) from loopy.type_inference import TypeReader from loopy.kernel.instruction import (MultiAssignmentBase, CallInstruction, CInstruction, _DataObliviousInstruction, @@ -537,7 +537,7 @@ class _AccessCheckMapper(WalkMapper): shape_domain = shape_domain.intersect(slab) if not access_range.is_subset(shape_domain): - raise LoopyError("'%s' in instruction '%s' " + raise LoopyIndexError("'%s' in instruction '%s' " "accesses out-of-bounds array element (could not" " establish '%s' is a subset of '%s')." % (expr, insn_id, access_range, shape_domain)) @@ -556,10 +556,7 @@ class _AccessCheckMapper(WalkMapper): self.rec(expr.else_, domain & else_set, insn_id) -def check_bounds(kernel): - """ - Performs out-of-bound check for every array access. - """ +def _check_bounds_inner(kernel): from loopy.kernel.instruction import get_insn_domain temp_var_names = set(kernel.temporary_variables) acm = _AccessCheckMapper(kernel) @@ -583,6 +580,34 @@ def check_bounds(kernel): insn.with_transformed_expressions(run_acm) + +def check_bounds(kernel): + """ + Performs out-of-bound check for every array access. + """ + if kernel.options.enforce_array_accesses_within_bounds not in [ + "no_check", + True, + False]: + raise LoopyError("invalid value for option " + "'enforce_array_accesses_within_bounds': %s" + % kernel.options.enforce_array_accesses_within_bounds) + + if kernel.options.enforce_array_accesses_within_bounds == "no_check": + return + + from pytools import ProcessLogger + with ProcessLogger(logger, "%s: check array access within bounds" % kernel.name): + if kernel.options.enforce_array_accesses_within_bounds: + _check_bounds_inner(kernel) + else: + from loopy.diagnostic import LoopyIndexError + try: + _check_bounds_inner(kernel) + except LoopyIndexError as e: + from loopy.diagnostic import warn_with_kernel + warn_with_kernel(kernel, "array_access_out_of_bounds", str(e)) + # }}} diff --git a/loopy/diagnostic.py b/loopy/diagnostic.py index c471facc80ca4a043a62dd5d96c6bf3ba4873226..fc8fbe71f8fbec10f31684107f012dc94201618d 100644 --- a/loopy/diagnostic.py +++ b/loopy/diagnostic.py @@ -74,6 +74,10 @@ class LoopyError(RuntimeError): pass +class LoopyIndexError(LoopyError): + pass + + class CannotBranchDomainTree(LoopyError): pass @@ -115,6 +119,11 @@ class LoopyTypeError(LoopyError): class ExpressionNotAffineError(LoopyError): + """ + Raised when an expression is not quasi-affine. See + `ISL manual <http://isl.gforge.inria.fr//user.html#Primitive-Functions>`_ + for then definition of a quasi-affine expression. + """ pass diff --git a/loopy/frontend/fortran/__init__.py b/loopy/frontend/fortran/__init__.py index d6311131f935a5b7dc8405e0cad039371131a079..15c6a7dc31f33d0897c4d6cd5293a273a52d2ee0 100644 --- a/loopy/frontend/fortran/__init__.py +++ b/loopy/frontend/fortran/__init__.py @@ -294,7 +294,15 @@ def _add_assignees_to_calls(knl, all_kernels): def parse_fortran(source, filename="<floopy code>", free_form=None, strict=None, - seq_dependencies=None, auto_dependencies=None, target=None): + seq_dependencies=None, auto_dependencies=None, target=None, + all_names_known=True): + """ + :arg all_names_known: if set to *False*, enter an undocumented mode + in which Fortran parsing will try to tolerate unknown names. + If used, ``loopy.frontend.fortran.translator.specialize_fortran_division`` + must be called as soon as all names are known. + :returns: a :class:`loopy.TranslationUnit` + """ parse_plog = ProcessLogger(logger, "parsing fortran file '%s'" % filename) @@ -330,7 +338,8 @@ def parse_fortran(source, filename="<floopy code>", free_form=None, strict=None, "and returned invalid data (Sorry!)") from loopy.frontend.fortran.translator import F2LoopyTranslator - f2loopy = F2LoopyTranslator(filename, target=target) + f2loopy = F2LoopyTranslator( + filename, target=target, all_names_known=all_names_known) f2loopy(tree) kernels = f2loopy.make_kernels(seq_dependencies=seq_dependencies) diff --git a/loopy/frontend/fortran/translator.py b/loopy/frontend/fortran/translator.py index 7109f1ed216ab8ede25dadbae076839644b8b268..8dcc32e008497703171c38a446d6e9031e9a708c 100644 --- a/loopy/frontend/fortran/translator.py +++ b/loopy/frontend/fortran/translator.py @@ -28,11 +28,13 @@ import loopy as lp import numpy as np from warnings import warn from loopy.frontend.fortran.tree import FTreeWalkerBase +from loopy.diagnostic import warn_with_kernel from loopy.frontend.fortran.diagnostic import ( TranslationError, TranslatorWarning) import islpy as isl from islpy import dim_type -from loopy.symbolic import IdentityMapper +from loopy.symbolic import (IdentityMapper, RuleAwareIdentityMapper, + SubstitutionRuleMappingContext) from loopy.diagnostic import LoopyError from loopy.kernel.instruction import LegacyStringInstructionTag from pymbolic.primitives import (Wildcard, Slice) @@ -124,7 +126,9 @@ class SubscriptIndexAdjuster(IdentityMapper): # {{{ scope class Scope: - def __init__(self, subprogram_name, arg_names=set()): + def __init__(self, subprogram_name, arg_names=None): + if arg_names is None: + arg_names = set() self.subprogram_name = subprogram_name # map first letter to type @@ -244,13 +248,66 @@ class Scope: # }}} +# {{{ fortran division specializers + +class FortranDivisionToFloorDiv(IdentityMapper): + def map_fortran_division(self, expr, *args): + from warnings import warn + from loopy.diagnostic import LoopyWarning + warn( + "Integer division in Fortran do loop bound. " + "Loopy currently forces this to integers and gets it wrong for " + "negative arguments.", LoopyWarning) + from pymbolic.primitives import FloorDiv + return FloorDiv( + self.rec(expr.numerator, *args), + self.rec(expr.denominator, *args)) + + +class FortranDivisionSpecializer(RuleAwareIdentityMapper): + def __init__(self, rule_mapping_context, kernel): + super().__init__(rule_mapping_context) + from loopy.type_inference import TypeInferenceMapper + self.infer_type = TypeInferenceMapper(kernel) + self.kernel = kernel + + def map_fortran_division(self, expr, *args): + # We remove all these before type inference ever sees them. + num_dtype = self.infer_type(expr.numerator).numpy_dtype + den_dtype = self.infer_type(expr.denominator).numpy_dtype + + from pymbolic.primitives import Quotient, FloorDiv + if num_dtype.kind in "iub" and den_dtype.kind in "iub": + warn_with_kernel(self.kernel, + "fortran_int_div", + "Integer division in Fortran code. Loopy currently gets this " + "wrong for negative arguments.") + return FloorDiv( + self.rec(expr.numerator, *args), + self.rec(expr.denominator, *args)) + + else: + return Quotient( + self.rec(expr.numerator, *args), + self.rec(expr.denominator, *args)) + + +def specialize_fortran_division(knl): + rmc = SubstitutionRuleMappingContext( + knl.substitutions, knl.get_var_name_generator()) + return FortranDivisionSpecializer(rmc, knl).map_kernel(knl) + +# }}} + + # {{{ translator class F2LoopyTranslator(FTreeWalkerBase): - def __init__(self, filename, target=None): + def __init__(self, filename, target=None, all_names_known=True): FTreeWalkerBase.__init__(self, filename) self.target = target + self.all_names_known = all_names_known self.scope_stack = [] @@ -433,7 +490,9 @@ class F2LoopyTranslator(FTreeWalkerBase): for name, data in node.stmts: name, = name assert name not in scope.data - scope.data[name] = [self.parse_expr(node, i) for i in data] + scope.data[name] = [ + scope.process_expression_for_loopy( + self.parse_expr(node, i)) for i in data] return [] @@ -564,7 +623,9 @@ class F2LoopyTranslator(FTreeWalkerBase): cond_var = var(cond_name) self.add_expression_instruction( - cond_var, self.parse_expr(node, node.expr)) + cond_var, + scope.process_expression_for_loopy( + self.parse_expr(node, node.expr))) cond_expr = cond_var if context_cond is not None: @@ -643,9 +704,10 @@ class F2LoopyTranslator(FTreeWalkerBase): % (loop_var, iname_dtype, self.index_dtype)) scope.use_name(loop_var) - loop_bounds = self.parse_expr( - node, - loop_bounds, min_precedence=self.expr_parser._PREC_FUNC_ARGS) + loop_bounds = scope.process_expression_for_loopy( + self.parse_expr( + node, + loop_bounds, min_precedence=self.expr_parser._PREC_FUNC_ARGS)) if len(loop_bounds) == 2: start, stop = loop_bounds @@ -710,7 +772,8 @@ class F2LoopyTranslator(FTreeWalkerBase): isl.Constraint.inequality_from_aff( iname_rel_aff(space, loopy_loop_var, "<=", - aff_from_expr(space, stop-start))))) + aff_from_expr(space, FortranDivisionToFloorDiv()( + stop-start)))))) from pymbolic import var scope.active_iname_aliases[loop_var] = \ @@ -839,8 +902,12 @@ class F2LoopyTranslator(FTreeWalkerBase): seq_dependencies=seq_dependencies, ) + if self.all_names_known: + knl = specialize_fortran_division(knl) + from loopy.loop import merge_loop_domains knl = merge_loop_domains(knl) + knl = lp.fold_constants(knl) result.append(knl) diff --git a/loopy/frontend/fortran/tree.py b/loopy/frontend/fortran/tree.py index 0dc426572f69b7a8ce16dbc97a70f874f17954c4..9a703a794ebb20a1881c1ced79258fa83b06ed04 100644 --- a/loopy/frontend/fortran/tree.py +++ b/loopy/frontend/fortran/tree.py @@ -23,6 +23,14 @@ THE SOFTWARE. import re from loopy.diagnostic import LoopyError +from loopy.symbolic import IdentityMapper, FortranDivision + + +class DivisionToFortranDivisionMapper(IdentityMapper): + def map_quotient(self, expr): + return FortranDivision( + self.rec(expr.numerator), + self.rec(expr.denominator)) class FTreeWalkerBase: @@ -110,7 +118,8 @@ class FTreeWalkerBase: def parse_expr(self, node, expr_str, **kwargs): try: - return self.expr_parser(expr_str, **kwargs) + return DivisionToFortranDivisionMapper()( + self.expr_parser(expr_str, **kwargs)) except Exception as e: raise LoopyError( "Error parsing expression '%s' on line %d of '%s': %s" diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 42cf4e0173b95ed8bce37509dafb3692bfc1c23d..aed6ae16818df49fab0b03be288b15b611ba30f7 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -383,6 +383,7 @@ class InstructionBase(ImmutableRecord, Taggable): return result + @memoize_method def dependency_names(self): return self.read_dependency_names() | self.write_dependency_names() diff --git a/loopy/options.py b/loopy/options.py index 45eb3eb63014ad48d0b3b42045c96913bc515c2c..086015dd6b0ef380eae9f56f41f0f487f8982a85 100644 --- a/loopy/options.py +++ b/loopy/options.py @@ -169,6 +169,14 @@ class Options(ImmutableRecord): helps find and eliminate unintentionally unordered access to variables. + If equal to ``"no_check"``, then no check is performed. + + .. attribute:: enforce_array_accesses_within_bounds + + If *True*, require that :func:`~loopy.check.check_bounds` passes. If + *False*, then :func:`~loopy.check.check_bounds` raises a warning for + any out-of-bounds accesses. + If equal to ``"no_check"``, then no check is performed. """ @@ -234,6 +242,8 @@ class Options(ImmutableRecord): enforce_variable_access_ordered=kwargs.get( "enforce_variable_access_ordered", True), + enforce_array_accesses_within_bounds=kwargs.get( + "enforce_array_accesses_within_bounds", True), ) # {{{ legacy compatibility diff --git a/loopy/symbolic.py b/loopy/symbolic.py index 29e22ab31139dbd46fa2df47654548e0991b2a49..9917de098786966a702b8948728f6aae33d835d3 100644 --- a/loopy/symbolic.py +++ b/loopy/symbolic.py @@ -157,6 +157,11 @@ class IdentityMapperMixin: map_rule_argument = map_group_hw_index + def map_fortran_division(self, expr, *args, **kwargs): + return type(expr)( + self.rec(expr.numerator, *args, **kwargs), + self.rec(expr.denominator, *args, **kwargs)) + class IdentityMapper(IdentityMapperBase, IdentityMapperMixin): pass @@ -221,6 +226,8 @@ class WalkMapper(WalkMapperBase): self.rec(expr.function, *args) + map_fortran_division = WalkMapperBase.map_quotient + class CallbackMapper(CallbackMapperBase, IdentityMapper): map_reduction = CallbackMapperBase.map_constant @@ -238,6 +245,8 @@ class CombineMapper(CombineMapperBase): map_linear_subscript = CombineMapperBase.map_subscript + map_fortran_division = CombineMapperBase.map_quotient + class SubstitutionMapper( CSECachingMapperMixin, SubstitutionMapperBase, IdentityMapperMixin): @@ -305,6 +314,11 @@ class StringifyMapper(StringifyMapperBase): expr.swept_inames), subscr=self.rec(expr.subscript, prec)) + def map_fortran_division(self, expr, enclosing_prec): + from pymbolic.mapper.stringifier import PREC_NONE + result = self.map_quotient(expr, PREC_NONE) + return f"[FORTRANDIV]({result})" + class EqualityPreservingStringifyMapper(StringifyMapperBase): """ @@ -407,6 +421,8 @@ class DependencyMapper(DependencyMapperBase): # See https://github.com/inducer/loopy/pull/323 raise NotImplementedError + map_fortran_division = DependencyMapperBase.map_quotient + class SubstitutionRuleExpander(IdentityMapper): def __init__(self, rules): @@ -962,6 +978,20 @@ class SubArrayRef(LoopyExpressionBase): mapper_method = intern("map_sub_array_ref") + +class FortranDivision(p.QuotientBase, LoopyExpressionBase): + """This exists for the benefit of the Fortran frontend, which specializes + to floating point division for floating point inputs and round-to-zero + division for integer inputs. Despite the name, this would also be usable + for C semantics. (:mod:`loopy` division semantics match Python's.) + + .. note:: + + This is not a documented expression node type. It may disappear + at any moment. + """ + mapper_method = "map_fortran_division" + # }}} @@ -1810,7 +1840,8 @@ def aff_from_expr(space, expr, vars_to_zero=None): (s, aff), = pieces return aff else: - raise RuntimeError("expression '%s' could not be converted to a " + from loopy.diagnostic import ExpressionNotAffineError + raise ExpressionNotAffineError("expression '%s' could not be converted to a " "non-piecewise quasi-affine expression" % expr) @@ -1821,6 +1852,7 @@ def pwaff_from_expr(space, expr, vars_to_zero=None): def with_aff_conversion_guard(f, space, expr, *args): import islpy as isl from pymbolic.mapper.evaluator import UnknownVariableError + from loopy.diagnostic import ExpressionNotAffineError err = None with isl.SuppressedWarnings(space.get_ctx()): @@ -1832,6 +1864,8 @@ def with_aff_conversion_guard(f, space, expr, *args): err = e except UnknownVariableError as e: err = e + except ExpressionNotAffineError as e: + err = e assert err is not None from loopy.diagnostic import ExpressionToAffineConversionError diff --git a/loopy/target/execution.py b/loopy/target/execution.py index 2f8335848660519833cf78a08a3b650d45cb16a1..fdd38278ecbd2d83727071006ccf08968edba46f 100644 --- a/loopy/target/execution.py +++ b/loopy/target/execution.py @@ -704,7 +704,7 @@ typed_and_scheduled_cache = WriteOncePersistentDict( invoker_cache = WriteOncePersistentDict( - "loopy-invoker-cache-v1-"+DATA_MODEL_VERSION, + "loopy-invoker-cache-v10-"+DATA_MODEL_VERSION, key_builder=LoopyKeyBuilder()) diff --git a/loopy/target/pyopencl.py b/loopy/target/pyopencl.py index bcced16df351d3b2d11592c9a781cd74b04ae1e9..5a26e8f0b2e0924aebead041ae543a3fc74c7a12 100644 --- a/loopy/target/pyopencl.py +++ b/loopy/target/pyopencl.py @@ -544,9 +544,9 @@ class PyOpenCLTarget(OpenCLTarget): use_int8_for_bool=use_int8_for_bool) import pyopencl.version - if pyopencl.version.VERSION < (2021, 1): + if pyopencl.version.VERSION < (2021, 2): raise RuntimeError("The version of loopy you have installed " - "generates invoker code that requires PyOpenCL 2021.1 " + "generates invoker code that requires PyOpenCL 2021.2 " "or newer.") self.device = device diff --git a/loopy/target/pyopencl_execution.py b/loopy/target/pyopencl_execution.py index 5ac37e1520abc774b877d16741302ff7b79810af..76bd323873d5ffd67e4d0d202f0f9e19b2b4cf9a 100644 --- a/loopy/target/pyopencl_execution.py +++ b/loopy/target/pyopencl_execution.py @@ -48,6 +48,8 @@ class PyOpenCLExecutionWrapperGenerator(ExecutionWrapperGeneratorBase): "out_host=None" ] super().__init__(system_args) + from pytools import UniqueNameGenerator + self.dtype_name_generator = UniqueNameGenerator(forced_prefix="_lpy_dtype_") def python_dtype_str(self, dtype): import pyopencl.tools as cl_tools @@ -90,47 +92,46 @@ class PyOpenCLExecutionWrapperGenerator(ExecutionWrapperGeneratorBase): from pymbolic import var num_axes = len(arg.strides) - for i in range(num_axes): - gen("_lpy_shape_%d = %s" % (i, strify(arg.unvec_shape[i]))) itemsize = kernel_arg.dtype.numpy_dtype.itemsize for i in range(num_axes): - gen("_lpy_strides_%d = %s" % (i, strify( - itemsize*arg.unvec_strides[i]))) + gen("_lpy_ustrides_%d = %s" % (i, strify( + arg.unvec_strides[i]))) if not skip_arg_checks: for i in range(num_axes): - gen("assert _lpy_strides_%d > 0, " + gen("assert _lpy_ustrides_%d > 0, " "\"'%s' has negative stride in axis %d\"" % (i, arg.name, i)) - sym_strides = tuple( - var("_lpy_strides_%d" % i) + sym_ustrides = tuple( + var("_lpy_ustrides_%d" % i) for i in range(num_axes)) sym_shape = tuple( - var("_lpy_shape_%d" % i) + arg.unvec_shape[i] for i in range(num_axes)) - alloc_size_expr = (sum(astrd*(alen-1) - for alen, astrd in zip(sym_shape, sym_strides)) - + itemsize) + size_expr = (sum(astrd*(alen-1) + for alen, astrd in zip(sym_shape, sym_ustrides)) + + 1) - gen("_lpy_alloc_size = %s" % strify(alloc_size_expr)) - gen("%(name)s = _lpy_cl_array.Array(queue, %(shape)s, " - "%(dtype)s, strides=%(strides)s, " - "data=allocator(_lpy_alloc_size), allocator=allocator)" - % dict( - name=arg.name, - shape=strify(sym_shape), - strides=strify(sym_strides), - dtype=self.python_dtype_str(kernel_arg.dtype.numpy_dtype))) + gen("_lpy_size = %s" % strify(size_expr)) + sym_strides = tuple(itemsize*s_i for s_i in sym_ustrides) + dtype_str = self.python_dtype_str(kernel_arg.dtype.numpy_dtype) - if not skip_arg_checks: - for i in range(num_axes): - gen("del _lpy_shape_%d" % i) - gen("del _lpy_strides_%d" % i) - gen("del _lpy_alloc_size") - gen("") + dtype_name = self.dtype_name_generator() + gen.add_to_preamble(f"{dtype_name} = _lpy_np.dtype({dtype_str})") + gen(f"{arg.name} = _lpy_cl_array.Array(None, {strify(sym_shape)}, " + f"{dtype_name}, strides={strify(sym_strides)}, " + f"data=allocator({strify(itemsize * var('_lpy_size'))}), " + "allocator=allocator, " + "_fast=True, _size=_lpy_size, " + "_context=queue.context, _queue=queue)") + + for i in range(num_axes): + gen("del _lpy_ustrides_%d" % i) + gen("del _lpy_size") + gen("") # }}} diff --git a/loopy/transform/iname.py b/loopy/transform/iname.py index 7f1273d3c7cecc80d69bad44f5cfb59078752f12..85588da3eba4bd259b031aa437ecb51d42bf91c5 100644 --- a/loopy/transform/iname.py +++ b/loopy/transform/iname.py @@ -310,7 +310,7 @@ def _split_iname_backend(kernel, iname_to_split, loop_priority=frozenset(new_priorities)) rule_mapping_context = SubstitutionRuleMappingContext( - kernel.substitutions, kernel.get_var_name_generator()) + kernel.substitutions, vng) ins = _InameSplitter(rule_mapping_context, within, iname_to_split, outer_iname, inner_iname, new_loop_index) diff --git a/test/test_fortran.py b/test/test_fortran.py index 72f7b7e016b91e869bfb103e574c5c2e450a943e..d6bb57162ca6ed4f9194e29491f53ebdaf9f1e14 100644 --- a/test/test_fortran.py +++ b/test/test_fortran.py @@ -654,6 +654,28 @@ def test_domain_fusion_imperfectly_nested(): assert len(t_unit["imperfect"].domains) > 1 +def test_division_in_shapes(ctx_factory): + fortran_src = """ + subroutine halve(m, a) + implicit none + integer m, i, j + real*8 a(m/2,m/2) + do i = 1,m/2 + do j = 1,m/2 + a(i, j) = 2*a(i, j) + end do + end do + end subroutine + """ + knl, = lp.parse_fortran(fortran_src) + ref_knl = knl + + print(knl) + + ctx = ctx_factory() + lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters=dict(m=128)) + + if __name__ == "__main__": if len(sys.argv) > 1: exec(sys.argv[1]) diff --git a/test/test_numa_diff.py b/test/test_numa_diff.py index 40309f70f15a0ffae55bcd7f406d2cc08d95ad46..bfe7c8756a3dc3b12e1dcdce635c5287665018ee 100644 --- a/test/test_numa_diff.py +++ b/test/test_numa_diff.py @@ -57,7 +57,8 @@ def test_gnuma_horiz_kernel(ctx_factory, ilp_multiple, Nq, opt_level): # noqa source = source.replace("datafloat", "real*4") - program = lp.parse_fortran(source, filename, seq_dependencies=False) + program = lp.parse_fortran(source, filename, seq_dependencies=False, + all_names_known=False) hsv_r, hsv_s = program["strongVolumeKernelR"], program["strongVolumeKernelS"] @@ -77,6 +78,9 @@ def test_gnuma_horiz_kernel(ctx_factory, ilp_multiple, Nq, opt_level): # noqa hsv = lp.assume(hsv, "elements >= 1") hsv = fix_euler_parameters(hsv, p_p0=1, p_Gamma=1.4, p_R=1) + from loopy.frontend.fortran.translator import specialize_fortran_division + hsv = specialize_fortran_division(hsv) + for name in ["Q", "rhsQ"]: hsv = set_q_storage_format(hsv, name) diff --git a/test/test_transform.py b/test/test_transform.py index 1e75aa0bc893be49b47b7a13067a0189920b9eac..4853545db20547483bcac86f1ec181434061ee53 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -50,7 +50,8 @@ __all__ = [ from loopy.version import LOOPY_USE_LANGUAGE_VERSION_2018_2 # noqa -def test_chunk_iname(ctx_factory): +@pytest.mark.parametrize("fix_parameters", (True, False)) +def test_chunk_iname(ctx_factory, fix_parameters): ctx = ctx_factory() knl = lp.make_kernel( @@ -65,7 +66,13 @@ def test_chunk_iname(ctx_factory): ref_knl = knl knl = lp.chunk_iname(knl, "i", 3, inner_tag="l.0") knl = lp.prioritize_loops(knl, "i_outer, i_inner") - lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters=dict(n=130)) + + if fix_parameters: + ref_knl = lp.fix_parameters(ref_knl, n=130) + knl = lp.fix_parameters(knl, n=130) + lp.auto_test_vs_ref(ref_knl, ctx, knl) + else: + lp.auto_test_vs_ref(ref_knl, ctx, knl, parameters={"n": 130}) def test_collect_common_factors(ctx_factory):