From c1085c290f3c142f26f46d6bb535b0757c75f427 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Wed, 21 Dec 2016 15:08:22 +0100
Subject: [PATCH 1/3] Add test for interaction of variable loop bounds with ILP
 [ci skip]

---
 test/test_loopy.py | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/test/test_loopy.py b/test/test_loopy.py
index 6b6071096..6d09e0ad4 100644
--- a/test/test_loopy.py
+++ b/test/test_loopy.py
@@ -1995,6 +1995,31 @@ def test_integer_reduction(ctx_factory):
             assert function(out)
 
 
+def test_ilp_modified_var_in_loop_bound(ctx_factory):
+    # https://github.com/inducer/loopy/issues/77
+
+    knl = lp.make_kernel([
+        "{ [i] : 0 <= i < m }",
+        "{ [j] : 0 <= j < length }"],
+        """
+        for i
+            <> rowstart = rowstarts[i]
+            <> rowend = rowstarts[i]
+            <> length = rowend - rowstart
+            y[i] = sum(j, values[rowstart+j] * x[colindices[rowstart + j]])
+        end
+        """)
+
+    knl = lp.add_and_infer_dtypes(
+            knl, {
+                'values,x': np.float64,
+                'rowstarts,colindices': knl.index_dtype})
+    knl = lp.split_iname(knl, 'i', 2, inner_tag='ilp')
+    code = lp.generate_code_v2(knl).device_code()
+    print(code.replace("length[", "LENGTH["))
+    assert 'length' not in code.replace("length[", "LENGTH[")
+
+
 if __name__ == "__main__":
     if len(sys.argv) > 1:
         exec(sys.argv[1])
-- 
GitLab


From 5e3ef098c1a792c0fda317a0d2e2083146f25838 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Fri, 30 Dec 2016 10:18:09 -0500
Subject: [PATCH 2/3] Save up ILP rewritings so that they can be applied to
 loop bounds and bound-like expressions

---
 loopy/codegen/bounds.py  |  7 +++++++
 loopy/codegen/control.py | 11 +++++++----
 loopy/codegen/loop.py    | 12 +++++++++---
 loopy/kernel/__init__.py | 10 +++++++++-
 loopy/transform/ilp.py   |  5 ++++-
 loopy/version.py         |  2 +-
 6 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/loopy/codegen/bounds.py b/loopy/codegen/bounds.py
index 7cc381f11..04599d119 100644
--- a/loopy/codegen/bounds.py
+++ b/loopy/codegen/bounds.py
@@ -27,6 +27,13 @@ import islpy as isl
 from islpy import dim_type
 
 
+def rewrite_loop_bound_expression(kernel, expr):
+    for rewriter in kernel.loop_bound_expression_rewriters:
+        expr = rewriter(expr)
+
+    return expr
+
+
 # {{{ approximate, convex bounds check generator
 
 def get_approximate_convex_bounds_checks(domain, check_inames, implemented_domain):
diff --git a/loopy/codegen/control.py b/loopy/codegen/control.py
index d206faad5..0128f1786 100644
--- a/loopy/codegen/control.py
+++ b/loopy/codegen/control.py
@@ -1,8 +1,6 @@
 """Loop nest build top-level control/hoisting."""
 
-from __future__ import division
-from __future__ import absolute_import
-import six
+from __future__ import division, absolute_import
 
 __copyright__ = "Copyright (C) 2012 Andreas Kloeckner"
 
@@ -27,6 +25,8 @@ THE SOFTWARE.
 """
 
 
+import six
+
 from loopy.codegen.result import merge_codegen_results, wrap_in_if
 import islpy as isl
 from loopy.schedule import (
@@ -464,12 +464,15 @@ def build_loop_nest(codegen_state, schedule_index):
 
             if bounds_checks or pred_checks:
                 from loopy.symbolic import constraint_to_cond_expr
+                from loopy.codegen.bounds import rewrite_loop_bound_expression
 
                 prev_gen_code = gen_code
 
                 def gen_code(inner_codegen_state):
                     condition_exprs = [
-                            constraint_to_cond_expr(cns)
+                            rewrite_loop_bound_expression(
+                                kernel,
+                                constraint_to_cond_expr(cns))
                             for cns in bounds_checks] + [
                                 pred_chk for pred_chk in pred_checks]
 
diff --git a/loopy/codegen/loop.py b/loopy/codegen/loop.py
index ad80475c1..e756b1ef8 100644
--- a/loopy/codegen/loop.py
+++ b/loopy/codegen/loop.py
@@ -349,7 +349,8 @@ def generate_sequential_loop_dim_code(codegen_state, sched_index):
 
     slabs = get_slab_decomposition(kernel, loop_iname)
 
-    from loopy.codegen.bounds import get_usable_inames_for_conditional
+    from loopy.codegen.bounds import (
+            get_usable_inames_for_conditional, rewrite_loop_bound_expression)
 
     # Note: this does not include loop_iname itself!
     usable_inames = get_usable_inames_for_conditional(kernel, sched_index)
@@ -451,7 +452,8 @@ def generate_sequential_loop_dim_code(codegen_state, sched_index):
                 astb.emit_initializer(
                     codegen_state,
                     kernel.index_dtype, loop_iname,
-                    ecm(pw_aff_to_expr(lbound), PREC_NONE, "i"),
+                    ecm(rewrite_loop_bound_expression(kernel,
+                        pw_aff_to_expr(lbound), PREC_NONE, "i")),
                     is_const=True),
                 astb.emit_blank_line(),
                 inner,
@@ -469,7 +471,11 @@ def generate_sequential_loop_dim_code(codegen_state, sched_index):
                     codegen_state,
                     astb.emit_sequential_loop(
                         codegen_state, loop_iname, kernel.index_dtype,
-                        pw_aff_to_expr(lbound), pw_aff_to_expr(ubound), inner_ast)))
+                        rewrite_loop_bound_expression(kernel,
+                            pw_aff_to_expr(lbound)),
+                        rewrite_loop_bound_expression(kernel,
+                            pw_aff_to_expr(ubound)),
+                        inner_ast)))
 
     return merge_codegen_results(codegen_state, result)
 
diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py
index 5dff5e53c..98a5feb83 100644
--- a/loopy/kernel/__init__.py
+++ b/loopy/kernel/__init__.py
@@ -147,6 +147,12 @@ class LoopKernel(ImmutableRecordWithoutPickling):
 
     .. attribute:: silenced_warnings
 
+    .. attribute:: loop_bound_expression_rewriters
+
+        A tuple of expression mappings that need to be applied to loop bound
+        expressions once generated. This is necessary, for example, to
+        capture ILP-based rewritings of data-dependent loop bounds.
+
     .. attribute:: applied_iname_rewrites
 
         A list of past substitution dictionaries that
@@ -189,6 +195,7 @@ class LoopKernel(ImmutableRecordWithoutPickling):
             silenced_warnings=[],
 
             applied_iname_rewrites=[],
+            loop_bound_expression_rewriters=(),
             cache_manager=None,
             index_dtype=np.int32,
             options=None,
@@ -279,7 +286,7 @@ class LoopKernel(ImmutableRecordWithoutPickling):
         assert all(dom.get_ctx() == isl.DEFAULT_CONTEXT for dom in domains)
         assert assumptions.get_ctx() == isl.DEFAULT_CONTEXT
 
-        ImmutableRecordWithoutPickling.__init__(self,
+        super(LoopKernel, self).__init__(
                 domains=domains,
                 instructions=instructions,
                 args=args,
@@ -297,6 +304,7 @@ class LoopKernel(ImmutableRecordWithoutPickling):
                 substitutions=substitutions,
                 cache_manager=cache_manager,
                 applied_iname_rewrites=applied_iname_rewrites,
+                loop_bound_expression_rewriters=loop_bound_expression_rewriters,
                 function_manglers=function_manglers,
                 symbol_manglers=symbol_manglers,
                 index_dtype=index_dtype,
diff --git a/loopy/transform/ilp.py b/loopy/transform/ilp.py
index 778407532..df8240ea3 100644
--- a/loopy/transform/ilp.py
+++ b/loopy/transform/ilp.py
@@ -170,7 +170,10 @@ def add_axes_to_temporaries_for_ilp_and_vec(kernel, iname=None):
 
     return kernel.copy(
         temporary_variables=new_temp_vars,
-        instructions=new_insns)
+        instructions=new_insns,
+        loop_bound_expression_rewriters=(
+            kernel.loop_bound_expression_rewriters
+            + (eiii,)))
 
 # }}}
 
diff --git a/loopy/version.py b/loopy/version.py
index b3505973b..fd7c66fa2 100644
--- a/loopy/version.py
+++ b/loopy/version.py
@@ -32,4 +32,4 @@ except ImportError:
 else:
     _islpy_version = islpy.version.VERSION_TEXT
 
-DATA_MODEL_VERSION = "v51-islpy%s" % _islpy_version
+DATA_MODEL_VERSION = "v54-islpy%s" % _islpy_version
-- 
GitLab


From c5e9c7dcc9157c0386856cfd16a8b73e1167ab17 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Fri, 30 Dec 2016 19:28:34 -0500
Subject: [PATCH 3/3] Fix rewrite_loop_bound_expression invocation

---
 loopy/codegen/loop.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/loopy/codegen/loop.py b/loopy/codegen/loop.py
index e756b1ef8..516d83858 100644
--- a/loopy/codegen/loop.py
+++ b/loopy/codegen/loop.py
@@ -452,8 +452,11 @@ def generate_sequential_loop_dim_code(codegen_state, sched_index):
                 astb.emit_initializer(
                     codegen_state,
                     kernel.index_dtype, loop_iname,
-                    ecm(rewrite_loop_bound_expression(kernel,
-                        pw_aff_to_expr(lbound), PREC_NONE, "i")),
+                    ecm(
+                        rewrite_loop_bound_expression(
+                            kernel,
+                            pw_aff_to_expr(lbound)),
+                        PREC_NONE, "i"),
                     is_const=True),
                 astb.emit_blank_line(),
                 inner,
-- 
GitLab