diff --git a/loopy/frontend/fortran/__init__.py b/loopy/frontend/fortran/__init__.py index 561a29cc816ba25372fb957311420b8c18291ac7..05b0a92050a51be1cd980648325921fbf13768d8 100644 --- a/loopy/frontend/fortran/__init__.py +++ b/loopy/frontend/fortran/__init__.py @@ -237,10 +237,23 @@ def parse_transformed_fortran(source, free_form=True, strict=True, def parse_fortran(source, filename="", free_form=True, strict=True, - auto_dependencies=True, target=None): + seq_dependencies=None, auto_dependencies=None, target=None): """ :returns: a list of :class:`loopy.LoopKernel` objects """ + + if seq_dependencies is not None and auto_dependencies is not None: + raise TypeError( + "may not specify both seq_dependencies and auto_dependencies") + if auto_dependencies is not None: + from warnings import warn + warn("auto_dependencies is deprecated, use seq_dependencies instead", + DeprecationWarning, stacklevel=2) + seq_dependencies = auto_dependencies + + if seq_dependencies is None: + seq_dependencies = True + import logging console = logging.StreamHandler() console.setLevel(logging.INFO) @@ -257,11 +270,10 @@ def parse_fortran(source, filename="", free_form=True, strict=True, "and returned invalid data (Sorry!)") from loopy.frontend.fortran.translator import F2LoopyTranslator - f2loopy = F2LoopyTranslator(filename, auto_dependencies=auto_dependencies, - target=target) + f2loopy = F2LoopyTranslator(filename, target=target) f2loopy(tree) - return f2loopy.make_kernels() + return f2loopy.make_kernels(seq_dependencies=seq_dependencies) # vim: foldmethod=marker diff --git a/loopy/frontend/fortran/translator.py b/loopy/frontend/fortran/translator.py index f0f978c43fec48e24d192f2626baf59e4657aa3d..50272e5fece9322db3f63104698e21f68c4f21db 100644 --- a/loopy/frontend/fortran/translator.py +++ b/loopy/frontend/fortran/translator.py @@ -198,10 +198,9 @@ class Scope(object): # {{{ translator class F2LoopyTranslator(FTreeWalkerBase): - def __init__(self, filename, auto_dependencies, target=None): + def __init__(self, filename, target=None): FTreeWalkerBase.__init__(self) - self.auto_dependencies = auto_dependencies self.target = target self.scope_stack = [] @@ -226,17 +225,11 @@ class F2LoopyTranslator(FTreeWalkerBase): new_id = intern("insn%d" % self.insn_id_counter) self.insn_id_counter += 1 - if self.auto_dependencies and scope.previous_instruction_id: - depends_on = frozenset([scope.previous_instruction_id]) - else: - depends_on = frozenset() - from loopy.kernel.data import Assignment insn = Assignment( lhs, rhs, within_inames=frozenset( scope.active_loopy_inames), - depends_on=depends_on, id=new_id, predicates=frozenset(self.conditions), tags=tuple(self.instruction_tags)) @@ -632,7 +625,7 @@ class F2LoopyTranslator(FTreeWalkerBase): # }}} - def make_kernels(self): + def make_kernels(self, seq_dependencies): result = [] for sub in self.kernels: @@ -682,6 +675,7 @@ class F2LoopyTranslator(FTreeWalkerBase): default_order="F", index_dtype=self.index_dtype, target=self.target, + seq_dependencies=seq_dependencies, ) from loopy.loop import fuse_loop_domains diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index f811812da57be5a2d5b6be5b35b8456800d2a05a..e0ec0df31dac5cf3ee470d5e6337060cd84a5099 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -1094,8 +1094,9 @@ class LoopKernel(RecordWithoutPickling): lines = [] - from loopy.preprocess import add_default_dependencies - kernel = add_default_dependencies(self) + from loopy.kernel.creation import apply_single_writer_depencency_heuristic + kernel = apply_single_writer_depencency_heuristic( + self, warn_if_used=False) sep = 75*"-" diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index 396b2b072b8c3567f09d0082a5e4c8190b742a21..ff3bf16bcf32b26b1865d350aefbef80ec4e4554 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -1191,11 +1191,16 @@ def add_sequential_dependencies(knl): new_insns = [] prev_insn = None for insn in knl.instructions: + depon = insn.depends_on + if depon is None: + depon = frozenset() + if prev_insn is not None: - depon = insn.depends_on - if depon is None: - depon = frozenset() - insn = insn.copy(depends_on=depon | frozenset((prev_insn.id,))) + depon = depon | frozenset((prev_insn.id,)) + + insn = insn.copy( + depends_on=depon, + depends_on_is_final=True) new_insns.append(insn) @@ -1484,6 +1489,76 @@ def add_inferred_inames(knl): # }}} +# {{{ apply single-writer heuristic + +def apply_single_writer_depencency_heuristic(kernel, warn_if_used=True): + logger.debug("%s: default deps" % kernel.name) + + from loopy.transform.subst import expand_subst + expanded_kernel = expand_subst(kernel) + + writer_map = kernel.writer_map() + + arg_names = set(arg.name for arg in kernel.args) + + var_names = arg_names | set(six.iterkeys(kernel.temporary_variables)) + + dep_map = dict( + (insn.id, insn.read_dependency_names() & var_names) + for insn in expanded_kernel.instructions) + + new_insns = [] + for insn in kernel.instructions: + if not insn.depends_on_is_final: + auto_deps = set() + + # {{{ add automatic dependencies + + all_my_var_writers = set() + for var in dep_map[insn.id]: + var_writers = writer_map.get(var, set()) + all_my_var_writers |= var_writers + + if not var_writers and var not in arg_names: + tv = kernel.temporary_variables[var] + if tv.initializer is None: + warn_with_kernel(kernel, "read_no_write(%s)" % var, + "temporary variable '%s' is read, but never written." + % var) + + if len(var_writers) == 1: + auto_deps.update( + var_writers + - set([insn.id])) + + # }}} + + depends_on = insn.depends_on + if depends_on is None: + depends_on = frozenset() + + new_deps = frozenset(auto_deps) | depends_on + + if warn_if_used and new_deps != depends_on: + warn_with_kernel(kernel, "single_writer_after_creation", + "The single-writer dependency heuristic added dependencies " + "on instruction ID(s) '%s' to instruction ID '%s' after " + "kernel creation is complete. This is deprecated and " + "may stop working in the future. " + "To fix this, ensure that instruction dependencies " + "are added/resolved as soon as possible, ideally at kernel " + "creation time." + % (", ".join(new_deps - depends_on), insn.id)) + + insn = insn.copy(depends_on=new_deps) + + new_insns.append(insn) + + return kernel.copy(instructions=new_insns) + +# }}} + + # {{{ kernel creation top-level def make_kernel(domains, instructions, kernel_data=["..."], **kwargs): @@ -1711,6 +1786,7 @@ def make_kernel(domains, instructions, kernel_data=["..."], **kwargs): knl = guess_arg_shape_if_requested(knl, default_order) knl = apply_default_order_to_args(knl, default_order) knl = resolve_wildcard_deps(knl) + knl = apply_single_writer_depencency_heuristic(knl, warn_if_used=False) # ------------------------------------------------------------------------- # Ordering dependency: diff --git a/loopy/kernel/tools.py b/loopy/kernel/tools.py index 350f4497c5880dd381e5071b06a25790a7cf82f6..54236efca651e676745c4198cf509b019b95c084 100644 --- a/loopy/kernel/tools.py +++ b/loopy/kernel/tools.py @@ -438,8 +438,8 @@ def get_dot_dependency_graph(kernel, iname_cluster=True, use_insn_id=False): """ # make sure all automatically added stuff shows up - from loopy.preprocess import add_default_dependencies - kernel = add_default_dependencies(kernel) + from loopy.kernel.creation import apply_single_writer_depencency_heuristic + kernel = apply_single_writer_depencency_heuristic(kernel, warn_if_used=False) if iname_cluster and not kernel.schedule: try: diff --git a/loopy/preprocess.py b/loopy/preprocess.py index db3be65f17473ed23bfb3ce36e3b95e35888df88..09437f213aa498d86a562f4363b15037fb396d90 100644 --- a/loopy/preprocess.py +++ b/loopy/preprocess.py @@ -436,63 +436,6 @@ def find_temporary_scope(kernel): # }}} -# {{{ default dependencies - -def add_default_dependencies(kernel): - logger.debug("%s: default deps" % kernel.name) - - from loopy.transform.subst import expand_subst - expanded_kernel = expand_subst(kernel) - - writer_map = kernel.writer_map() - - arg_names = set(arg.name for arg in kernel.args) - - var_names = arg_names | set(six.iterkeys(kernel.temporary_variables)) - - dep_map = dict( - (insn.id, insn.read_dependency_names() & var_names) - for insn in expanded_kernel.instructions) - - new_insns = [] - for insn in kernel.instructions: - if not insn.depends_on_is_final: - auto_deps = set() - - # {{{ add automatic dependencies - - all_my_var_writers = set() - for var in dep_map[insn.id]: - var_writers = writer_map.get(var, set()) - all_my_var_writers |= var_writers - - if not var_writers and var not in arg_names: - tv = kernel.temporary_variables[var] - if tv.initializer is None: - warn_with_kernel(kernel, "read_no_write(%s)" % var, - "temporary variable '%s' is read, but never written." - % var) - - if len(var_writers) == 1: - auto_deps.update( - var_writers - - set([insn.id])) - - # }}} - - depends_on = insn.depends_on - if depends_on is None: - depends_on = frozenset() - - insn = insn.copy(depends_on=frozenset(auto_deps) | depends_on) - - new_insns.append(insn) - - return kernel.copy(instructions=new_insns) - -# }}} - - # {{{ rewrite reduction to imperative form def realize_reduction(kernel, insn_id_filter=None, unknown_types_ok=True): @@ -1119,7 +1062,8 @@ def preprocess_kernel(kernel, device=None): check_reduction_iname_uniqueness(kernel) - kernel = add_default_dependencies(kernel) + from loopy.kernel.creation import apply_single_writer_depencency_heuristic + kernel = apply_single_writer_depencency_heuristic(kernel) # Ordering restrictions: # diff --git a/loopy/transform/diff.py b/loopy/transform/diff.py index d972f44cf6cdd4497c03b2725c4fe607f0e35842..d4dcb3701f4f23a5b1c66b1559bf6c4879425902 100644 --- a/loopy/transform/diff.py +++ b/loopy/transform/diff.py @@ -370,8 +370,8 @@ def diff_kernel(knl, diff_outputs, by, diff_iname_prefix="diff_i", *diff_context.by_name*, or *None* if no dependency exists. """ - from loopy.preprocess import add_default_dependencies - knl = add_default_dependencies(knl) + from loopy.kernel.creation import apply_single_writer_depencency_heuristic + knl = apply_single_writer_depencency_heuristic(knl, warn_if_used=True) if isinstance(diff_outputs, str): diff_outputs = [ diff --git a/loopy/transform/subst.py b/loopy/transform/subst.py index 27e98383ee7211643aecdafebc9bf63988c55e0c..b2b76ae9f3a92d93feca2dc9b31591f215b9341e 100644 --- a/loopy/transform/subst.py +++ b/loopy/transform/subst.py @@ -311,8 +311,8 @@ def assignment_to_subst(kernel, lhs_name, extra_arguments=(), within=None, # {{{ establish the relevant definition of lhs_name for each usage site dep_kernel = expand_subst(kernel) - from loopy.preprocess import add_default_dependencies - dep_kernel = add_default_dependencies(dep_kernel) + from loopy.kernel.creation import apply_single_writer_depencency_heuristic + dep_kernel = apply_single_writer_depencency_heuristic(dep_kernel) id_to_insn = dep_kernel.id_to_insn