From 070a64837adae8d33609e5c4c2c8ee49e184b975 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner <inform@tiker.net> Date: Thu, 29 Mar 2012 01:36:41 -0400 Subject: [PATCH] Expand CSEs. Factor make_kernel into multiple parts in a separate file. --- MEMO | 2 + loopy/__init__.py | 152 +--------------------------- loopy/creation.py | 245 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 148 deletions(-) create mode 100644 loopy/creation.py diff --git a/MEMO b/MEMO index ad071836c..a0f7f32ff 100644 --- a/MEMO +++ b/MEMO @@ -57,6 +57,8 @@ To-do - Scalar insn priority +- What to do about constants in codegen? (...f suffix, complex types) + - If finding a maximum proves troublesome, move parameters into the domain - : (as in, Matlab full-sclice) in prefetches diff --git a/loopy/__init__.py b/loopy/__init__.py index 78342b9e9..3c72b50e4 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -22,7 +22,9 @@ class LoopyAdvisory(UserWarning): from loopy.kernel import ScalarArg, ArrayArg, ConstantArrayArg, ImageArg -from loopy.kernel import AutoFitLocalIndexTag, get_dot_dependency_graph, LoopKernel +from loopy.kernel import (AutoFitLocalIndexTag, get_dot_dependency_graph, + LoopKernel, Instruction) +from loopy.creation import make_kernel from loopy.subst import extract_subst, expand_subst from loopy.cse import precompute from loopy.preprocess import preprocess_kernel, realize_reduction @@ -32,6 +34,7 @@ from loopy.compiled import CompiledKernel, drive_timing_run, auto_test_vs_ref from loopy.check import check_kernels __all__ = ["ScalarArg", "ArrayArg", "ConstantArrayArg", "ImageArg", "LoopKernel", + "Instruction", "make_kernel", "get_dot_dependency_graph", "preprocess_kernel", "realize_reduction", "generate_loop_schedules", @@ -45,153 +48,6 @@ __all__ = ["ScalarArg", "ArrayArg", "ConstantArrayArg", "ImageArg", "LoopKernel" # }}} -# {{{ kernel creation - -def make_kernel(*args, **kwargs): - """Second pass of kernel creation. Think about requests for iname duplication - and temporary variable declaration received as part of string instructions. - """ - - knl = LoopKernel(*args, **kwargs) - - knl = tag_dimensions( - knl.copy(iname_to_tag_requests=None), - knl.iname_to_tag_requests) - - new_insns = [] - new_domain = knl.domain - new_temp_vars = knl.temporary_variables.copy() - new_iname_to_tag = knl.iname_to_tag.copy() - - newly_created_vars = set() - - for insn in knl.instructions: - - # {{{ sanity checking - - if not set(insn.forced_iname_deps) <= knl.all_inames(): - raise ValueError("In instruction '%s': " - "cannot force dependency on inames '%s'--" - "they don't exist" % ( - insn.id, - ",".join( - set(insn.forced_iname_deps)-knl.all_inames()))) - - # }}} - - # {{{ iname duplication - - if insn.duplicate_inames_and_tags: - insn_dup_iname_to_tag = dict(insn.duplicate_inames_and_tags) - - if not set(insn_dup_iname_to_tag.keys()) <= knl.all_inames(): - raise ValueError("In instruction '%s': " - "cannot duplicate inames '%s'--" - "they don't exist" % ( - insn.id, - ",".join( - set(insn_dup_iname_to_tag.keys())-knl.all_inames()))) - - # {{{ duplicate non-reduction inames - - reduction_inames = insn.reduction_inames() - - inames_to_duplicate = [iname - for iname, tag in insn.duplicate_inames_and_tags - if iname not in reduction_inames] - - new_inames = [ - knl.make_unique_var_name( - based_on=iname+"_"+insn.id, - extra_used_vars= - newly_created_vars) - for iname in inames_to_duplicate] - - for old_iname, new_iname in zip(inames_to_duplicate, new_inames): - new_tag = insn_dup_iname_to_tag[old_iname] - if new_tag is None: - new_tag = AutoFitLocalIndexTag() - new_iname_to_tag[new_iname] = new_tag - - newly_created_vars.update(new_inames) - - from loopy.isl_helpers import duplicate_axes - new_domain = duplicate_axes(new_domain, inames_to_duplicate, new_inames) - - from loopy.symbolic import SubstitutionMapper - from pymbolic.mapper.substitutor import make_subst_func - from pymbolic import var - old_to_new = dict( - (old_iname, var(new_iname)) - for old_iname, new_iname in zip(inames_to_duplicate, new_inames)) - subst_map = SubstitutionMapper(make_subst_func(old_to_new)) - new_expression = subst_map(insn.expression) - - # }}} - - if len(inames_to_duplicate) < len(insn.duplicate_inames_and_tags): - raise RuntimeError("cannot use [|...] syntax to rename reduction " - "inames") - - insn = insn.copy( - assignee=subst_map(insn.assignee), - expression=new_expression, - forced_iname_deps=set( - old_to_new.get(iname, iname) for iname in insn.forced_iname_deps), - duplicate_inames_and_tags=[]) - - # }}} - - # {{{ temporary variable creation - - from loopy.kernel import ( - find_var_base_indices_and_shape_from_inames, - TemporaryVariable) - - if insn.temp_var_type is not None: - assignee_name = insn.get_assignee_var_name() - - assignee_indices = [] - from pymbolic.primitives import Variable - for index_expr in insn.get_assignee_indices(): - if (not isinstance(index_expr, Variable) - or not index_expr.name in knl.all_inames()): - raise RuntimeError( - "only plain inames are allowed in " - "the lvalue index when declaring the " - "variable '%s' in an instruction" - % assignee_name) - - assignee_indices.append(index_expr.name) - - base_indices, shape = \ - find_var_base_indices_and_shape_from_inames( - new_domain, assignee_indices, knl.cache_manager) - - new_temp_vars[assignee_name] = TemporaryVariable( - name=assignee_name, - dtype=np.dtype(insn.temp_var_type), - is_local=None, - base_indices=base_indices, - shape=shape) - - newly_created_vars.add(assignee_name) - - insn = insn.copy(temp_var_type=None) - - # }}} - - new_insns.append(insn) - - return knl.copy( - instructions=new_insns, - domain=new_domain, - temporary_variables=new_temp_vars, - iname_to_tag=new_iname_to_tag, - iname_to_tag_requests=[]) - -# }}} - # {{{ dimension split def split_dimension(kernel, split_iname, inner_length, diff --git a/loopy/creation.py b/loopy/creation.py new file mode 100644 index 000000000..3ca1991fa --- /dev/null +++ b/loopy/creation.py @@ -0,0 +1,245 @@ +from __future__ import division +import numpy as np +from loopy.symbolic import IdentityMapper + + +# {{{ sanity checking + +def check_kernel(knl): + for insn in knl.instructions: + if not set(insn.forced_iname_deps) <= knl.all_inames(): + raise ValueError("In instruction '%s': " + "cannot force dependency on inames '%s'--" + "they don't exist" % ( + insn.id, + ",".join( + set(insn.forced_iname_deps)-knl.all_inames()))) + +# }}} + +# {{{ expand common subexpressions into assignments + +class CSEToAssignmentMapper(IdentityMapper): + def __init__(self, add_assignment): + self.add_assignment = add_assignment + self.expr_to_var = {} + + def map_common_subexpression(self, expr): + try: + return self.expr_to_var[expr.child] + except KeyError: + from loopy.symbolic import TypedCSE + if isinstance(expr, TypedCSE): + dtype = expr.dtype + else: + dtype = None + + child = self.rec(expr.child) + + var_name = self.add_assignment(expr.prefix, child, dtype) + from pymbolic.primitives import Variable + var = Variable(var_name) + self.expr_to_var[expr.child] = var + return var + +def expand_cses(knl): + def add_assignment(base_name, expr, dtype): + kwargs = dict(extra_used_vars=newly_created_vars) + if base_name is not None: + kwargs["based_on"] = base_name + new_var_name = knl.make_unique_var_name(**kwargs) + newly_created_vars.add(new_var_name) + + if dtype is None: + dtype = tim(expr) + + from loopy.kernel import TemporaryVariable + new_temp_vars[new_var_name] = TemporaryVariable( + name=new_var_name, + dtype=np.dtype(dtype), + is_local=None, + shape=()) + + from pymbolic.primitives import Variable + from loopy.kernel import Instruction + insn = Instruction( + id=knl.make_unique_instruction_id(extra_used_ids=newly_created_insn_ids), + assignee=Variable(new_var_name), expression=expr) + newly_created_insn_ids.add(insn.id) + new_insns.append(insn) + + return new_var_name + + cseam = CSEToAssignmentMapper(add_assignment=add_assignment) + + new_insns = [] + + newly_created_vars = set() + newly_created_insn_ids = set() + new_temp_vars = knl.temporary_variables.copy() + + from loopy.codegen.expression import TypeInferenceMapper + tim = TypeInferenceMapper(knl, new_temp_vars) + + for insn in knl.instructions: + new_insns.append(insn.copy(expression=cseam(insn.expression))) + + return knl.copy( + instructions=new_insns, + temporary_variables=new_temp_vars) + +# }}} + +# {{{ temporary variable creation + +def create_temporaries(knl): + new_insns = [] + new_temp_vars = knl.temporary_variables.copy() + + for insn in knl.instructions: + from loopy.kernel import ( + find_var_base_indices_and_shape_from_inames, + TemporaryVariable) + + if insn.temp_var_type is not None: + assignee_name = insn.get_assignee_var_name() + + assignee_indices = [] + from pymbolic.primitives import Variable + for index_expr in insn.get_assignee_indices(): + if (not isinstance(index_expr, Variable) + or not index_expr.name in knl.all_inames()): + raise RuntimeError( + "only plain inames are allowed in " + "the lvalue index when declaring the " + "variable '%s' in an instruction" + % assignee_name) + + assignee_indices.append(index_expr.name) + + base_indices, shape = \ + find_var_base_indices_and_shape_from_inames( + knl.domain, assignee_indices, knl.cache_manager) + + new_temp_vars[assignee_name] = TemporaryVariable( + name=assignee_name, + dtype=np.dtype(insn.temp_var_type), + is_local=None, + base_indices=base_indices, + shape=shape) + + insn = insn.copy(temp_var_type=None) + + new_insns.append(insn) + + return knl.copy( + instructions=new_insns, + temporary_variables=new_temp_vars) + +# }}} + +# {{{ duplicate inames + +def duplicate_inames(knl): + from loopy.kernel import AutoFitLocalIndexTag + + new_insns = [] + new_domain = knl.domain + new_iname_to_tag = knl.iname_to_tag.copy() + + newly_created_vars = set() + + for insn in knl.instructions: + if insn.duplicate_inames_and_tags: + insn_dup_iname_to_tag = dict(insn.duplicate_inames_and_tags) + + if not set(insn_dup_iname_to_tag.keys()) <= knl.all_inames(): + raise ValueError("In instruction '%s': " + "cannot duplicate inames '%s'--" + "they don't exist" % ( + insn.id, + ",".join( + set(insn_dup_iname_to_tag.keys())-knl.all_inames()))) + + # {{{ duplicate non-reduction inames + + reduction_inames = insn.reduction_inames() + + inames_to_duplicate = [iname + for iname, tag in insn.duplicate_inames_and_tags + if iname not in reduction_inames] + + new_inames = [ + knl.make_unique_var_name( + based_on=iname+"_"+insn.id, + extra_used_vars=newly_created_vars) + for iname in inames_to_duplicate] + + for old_iname, new_iname in zip(inames_to_duplicate, new_inames): + new_tag = insn_dup_iname_to_tag[old_iname] + if new_tag is None: + new_tag = AutoFitLocalIndexTag() + new_iname_to_tag[new_iname] = new_tag + + newly_created_vars.update(new_inames) + + from loopy.isl_helpers import duplicate_axes + new_domain = duplicate_axes(new_domain, inames_to_duplicate, new_inames) + + from loopy.symbolic import SubstitutionMapper + from pymbolic.mapper.substitutor import make_subst_func + from pymbolic import var + old_to_new = dict( + (old_iname, var(new_iname)) + for old_iname, new_iname in zip(inames_to_duplicate, new_inames)) + subst_map = SubstitutionMapper(make_subst_func(old_to_new)) + new_expression = subst_map(insn.expression) + + # }}} + + if len(inames_to_duplicate) < len(insn.duplicate_inames_and_tags): + raise RuntimeError("cannot use [|...] syntax to rename reduction " + "inames") + + insn = insn.copy( + assignee=subst_map(insn.assignee), + expression=new_expression, + forced_iname_deps=set( + old_to_new.get(iname, iname) for iname in insn.forced_iname_deps), + duplicate_inames_and_tags=[]) + + new_insns.append(insn) + + return knl.copy( + instructions=new_insns, + domain=new_domain, + iname_to_tag=new_iname_to_tag) +# }}} + +# {{{ kernel creation top-level + +def make_kernel(*args, **kwargs): + """Second pass of kernel creation. Think about requests for iname duplication + and temporary variable declaration received as part of string instructions. + """ + + from loopy.kernel import LoopKernel + knl = LoopKernel(*args, **kwargs) + + from loopy import tag_dimensions + knl = tag_dimensions( + knl.copy(iname_to_tag_requests=None), + knl.iname_to_tag_requests).copy( + iname_to_tag_requests=[]) + + check_kernel(knl) + + knl = create_temporaries(knl) + knl = expand_cses(knl) + knl = duplicate_inames(knl) + + return knl + +# }}} + +# vim: fdm=marker -- GitLab