diff --git a/loopy/check.py b/loopy/check.py index a871508b9ddfa320d219c4767f22b6257a95a726..8022d91eed51cc6aa07ecb7d4910f33fb855fb9e 100644 --- a/loopy/check.py +++ b/loopy/check.py @@ -26,13 +26,11 @@ THE SOFTWARE. from islpy import dim_type import islpy as isl from loopy.symbolic import WalkMapper -from loopy.diagnostic import LoopyError, LoopyWarning +from loopy.diagnostic import LoopyError, WriteRaceConditionWarning, warn import logging logger = logging.getLogger(__name__) -from loopy.diagnostic import WriteRaceConditionError - # {{{ sanity checks run during scheduling @@ -167,11 +165,12 @@ def check_for_write_races(kernel): raceable_parallel_insn_inames - assignee_inames if race_inames: - raise WriteRaceConditionError( + warn(kernel, "write_race(%s)" % insn.id, "instruction '%s' contains a write race: " "instruction will be run across parallel iname(s) " "'%s', which is/are not referenced in the lhs index" - % (insn.id, ",".join(race_inames))) + % (insn.id, ",".join(race_inames)), + WriteRaceConditionWarning) def check_for_orphaned_user_hardware_axes(kernel): diff --git a/loopy/diagnostic.py b/loopy/diagnostic.py index dd2860a0e882e2567b508803ad94d945d0248ee4..e305038b89c4312ff415357a760cb08cd7b20135 100644 --- a/loopy/diagnostic.py +++ b/loopy/diagnostic.py @@ -40,9 +40,24 @@ class LoopyAdvisory(LoopyWarningBase): class ParameterFinderWarning(LoopyWarningBase): pass + +class WriteRaceConditionWarning(LoopyWarningBase): + pass + # }}} +def warn(kernel, id, text, type=LoopyWarning): + if id in kernel.silenced_warnings: + return + + text += (" (add '%s' to silenced_warnings kernel argument to disable)" + % id) + + from warnings import warn + warn(text, type) + + # {{{ errors class LoopyError(RuntimeError): @@ -53,10 +68,6 @@ class CannotBranchDomainTree(LoopyError): pass -class WriteRaceConditionError(LoopyError): - pass - - class TypeInferenceFailure(LoopyError): pass @@ -66,4 +77,7 @@ class DependencyTypeInferenceFailure(TypeInferenceFailure): # }}} + + + # vim: foldmethod=marker diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index 8a7c53f8460471aca3f68d549a0f17de1b48fe4b..423dba4e6e987526cb45442532f86b055272b443 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -124,6 +124,8 @@ class LoopKernel(Record): it will be scheduled. (This applies to inames with non-parallel implementation tags.) + .. attribute:: silenced_warnings + .. attribute:: applied_iname_rewrites A list of past substitution dictionaries that @@ -154,6 +156,8 @@ class LoopKernel(Record): iname_slab_increments={}, loop_priority=[], + silenced_warnings=[], + applied_iname_rewrites=[], cache_manager=None, index_dtype=np.int32, @@ -250,6 +254,7 @@ class LoopKernel(Record): assumptions=assumptions, iname_slab_increments=iname_slab_increments, loop_priority=loop_priority, + silenced_warnings=silenced_warnings, temporary_variables=temporary_variables, local_sizes=local_sizes, iname_to_tag=iname_to_tag, diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index 157b7cbf353edfa5a6d894dee99cdcae573f8693..91a654c3787b965ba2127b9669593f1a4001ce25 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -401,9 +401,10 @@ def guess_kernel_args_if_requested(domains, instructions, temporary_variables, temp_var_names = set(temporary_variables.iterkeys()) for insn in instructions: - if insn.temp_var_type is not None: - (assignee_var_name, _), = insn.assignees_and_indices() - temp_var_names.add(assignee_var_name) + if isinstance(insn, ExpressionInstruction): + if insn.temp_var_type is not None: + (assignee_var_name, _), = insn.assignees_and_indices() + temp_var_names.add(assignee_var_name) # }}} @@ -430,7 +431,7 @@ def guess_kernel_args_if_requested(domains, instructions, temporary_variables, all_params.update(dom.get_var_names(dim_type.param)) all_params = all_params - all_inames - new_arg_names = (all_names - not_new_arg_names) | all_params + new_arg_names = (all_names | all_params) - not_new_arg_names # }}} @@ -438,8 +439,8 @@ def guess_kernel_args_if_requested(domains, instructions, temporary_variables, irf = IndexRankFinder(name) for insn in instructions: - irf(submap(insn.expression, insn.id)) - irf(submap(insn.assignee, insn.id)) + insn.with_transformed_expressions( + lambda expr: irf(submap(expr, insn.id))) if not irf.index_ranks: return 0 @@ -880,11 +881,17 @@ def make_kernel(device, domains, instructions, kernel_data=["..."], **kwargs): :arg local_sizes: A dictionary from integers to integers, mapping workgroup axes to their sizes, e.g. *{0: 16}* forces axis 0 to be length 16. + :arg silenced_warnings: a list (or semicolon-separated string) or warnings + to silence """ defines = kwargs.pop("defines", {}) default_order = kwargs.pop("default_order", "C") default_offset = kwargs.pop("default_offset", 0) + silenced_warnings = kwargs.pop("silenced_warnings", []) + + if isinstance(silenced_warnings, str): + silenced_warnings = silenced_warnings.split(";") # {{{ separate temporary variables and arguments diff --git a/loopy/kernel/data.py b/loopy/kernel/data.py index c844995d61e590237d2203be9becbd86adc57b12..832096a32594e03c5263f2b143715e9c5a022aa8 100644 --- a/loopy/kernel/data.py +++ b/loopy/kernel/data.py @@ -556,8 +556,7 @@ def _remove_common_indentation(code): code = code.lstrip("//CL//") if not code.startswith("\n"): - raise ValueError("expected newline as first character " - "in literal lines") + return code lines = code.split("\n") while lines[0].strip() == "": diff --git a/loopy/preprocess.py b/loopy/preprocess.py index d41a36405c7a0e556860c797d2a203b43c1bca5d..c9adbf0c9354add723a2966ef18599813a05e85b 100644 --- a/loopy/preprocess.py +++ b/loopy/preprocess.py @@ -25,7 +25,9 @@ THE SOFTWARE. import pyopencl as cl import pyopencl.characterize as cl_char -from loopy.diagnostic import LoopyError, LoopyWarning +from loopy.diagnostic import ( + LoopyError, LoopyWarning, WriteRaceConditionWarning, warn, + LoopyAdvisory) import logging logger = logging.getLogger(__name__) @@ -250,15 +252,16 @@ def mark_local_temporaries(kernel): if (locparallel_assignee_inames != locparallel_compute_inames and bool(locparallel_assignee_inames)): - from loopy.check import WriteRaceConditionError - raise WriteRaceConditionError("instruction '%s' looks invalid: " + warn(kernel, "write_race_local(%s)" % insn_id, + "instruction '%s' looks invalid: " "it assigns to indices based on local IDs, but " "its temporary '%s' cannot be made local because " "a write race across the iname(s) '%s' would emerge. " "(Do you need to add an extra iname to your prefetch?)" % (insn_id, temp_var.name, ", ".join( locparallel_compute_inames - - locparallel_assignee_inames))) + - locparallel_assignee_inames)), + WriteRaceConditionWarning) wants_to_be_local_per_insn.append( locparallel_assignee_inames == locparallel_compute_inames @@ -268,9 +271,8 @@ def mark_local_temporaries(kernel): and bool(locparallel_compute_inames)) if not wants_to_be_local_per_insn: - from warnings import warn - from loopy.diagnostic import LoopyAdvisory - warn("temporary variable '%s' never written, eliminating" + warn(kernel, "temp_to_write(%s)" % temp_var.name, + "temporary variable '%s' never written, eliminating" % temp_var.name, LoopyAdvisory) is_local = wants_to_be_local_per_insn[0] diff --git a/test/test_linalg.py b/test/test_linalg.py index 374b43e6f2f1c75887800df541bdf5bdb88b21ab..6cbc81d4649107c7a67212eabdc350cef780685e 100644 --- a/test/test_linalg.py +++ b/test/test_linalg.py @@ -23,6 +23,8 @@ THE SOFTWARE. """ +import pytest +import sys import numpy as np import pyopencl as cl import pyopencl.array as cl_array @@ -490,6 +492,7 @@ def test_image_matrix_mul_ilp(ctx_factory): parameters={}) +@pytest.mark.skipif("sys.version_info < (2,6)") def test_ilp_race_matmul(ctx_factory): dtype = np.float32 ctx = ctx_factory() @@ -513,11 +516,14 @@ def test_ilp_race_matmul(ctx_factory): knl = lp.split_iname(knl, "k", 2) knl = lp.add_prefetch(knl, 'b', ["k_inner"]) - from loopy.check import WriteRaceConditionError - import pytest - with pytest.raises(WriteRaceConditionError): + from loopy.diagnostic import WriteRaceConditionWarning + from warnings import catch_warnings + with catch_warnings(record=True) as warn_list: list(lp.generate_loop_schedules(knl)) + assert any(isinstance(w.message, WriteRaceConditionWarning) + for w in warn_list) + def test_fancy_matrix_mul(ctx_factory): dtype = np.float32 @@ -587,7 +593,6 @@ def test_small_batched_matvec(ctx_factory): if __name__ == "__main__": - import sys if len(sys.argv) > 1: exec(sys.argv[1]) else: diff --git a/test/test_loopy.py b/test/test_loopy.py index e6d8a61adb303665c630387eda10bb761e82feeb..111c596867b1a0bcb52432add7a069d5a9ca401f 100644 --- a/test/test_loopy.py +++ b/test/test_loopy.py @@ -23,10 +23,12 @@ THE SOFTWARE. """ +import sys import numpy as np import loopy as lp import pyopencl as cl import pyopencl.clrandom # noqa +import pytest import logging logger = logging.getLogger(__name__) @@ -895,6 +897,7 @@ def test_double_sum(ctx_factory): # {{{ test race detection +@pytest.mark.skipif("sys.version_info < (2,6)") def test_ilp_write_race_detection_global(ctx_factory): ctx = ctx_factory() @@ -912,11 +915,14 @@ def test_ilp_write_race_detection_global(ctx_factory): knl = lp.tag_inames(knl, dict(j="ilp")) - from loopy.check import WriteRaceConditionError - import pytest - with pytest.raises(WriteRaceConditionError): + from loopy.diagnostic import WriteRaceConditionWarning + from warnings import catch_warnings + with catch_warnings(record=True) as warn_list: list(lp.generate_loop_schedules(knl)) + assert any(isinstance(w.message, WriteRaceConditionWarning) + for w in warn_list) + def test_ilp_write_race_avoidance_local(ctx_factory): ctx = ctx_factory() @@ -1192,7 +1198,6 @@ def test_c_instruction(ctx_factory): if __name__ == "__main__": - import sys if len(sys.argv) > 1: exec(sys.argv[1]) else: