[polly] r288489 - [ScopInfo] Fold constant coefficients in array dimensions to the right

Tobias Grosser via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 2 00:10:56 PST 2016


Author: grosser
Date: Fri Dec  2 02:10:56 2016
New Revision: 288489

URL: http://llvm.org/viewvc/llvm-project?rev=288489&view=rev
Log:
[ScopInfo] Fold constant coefficients in array dimensions to the right

This allows us to delinearize code such as the one below, where the array
sizes are A[][2 * n] as there are n times two elements in the innermost
dimension. Alternatively, we could try to generate another dimension for the
struct in the innermost dimension, but as the struct has constant size,
recovering this dimension is easy.

   struct com {
     double Real;
     double Img;
   };

   void foo(long n, struct com A[][n]) {
     for (long i = 0; i < 100; i++)
       for (long j = 0; j < 1000; j++)
         A[i][j].Real += A[i][j].Img;
   }

   int main() {
     struct com A[100][1000];
     foo(1000, A);

Added:
    polly/trunk/test/ScopInfo/multidim_fold_constant_dim.ll
Modified:
    polly/trunk/include/polly/ScopInfo.h
    polly/trunk/lib/Analysis/ScopInfo.cpp
    polly/trunk/test/Isl/CodeGen/invariant-load-dimension.ll

Modified: polly/trunk/include/polly/ScopInfo.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/include/polly/ScopInfo.h?rev=288489&r1=288488&r2=288489&view=diff
==============================================================================
--- polly/trunk/include/polly/ScopInfo.h (original)
+++ polly/trunk/include/polly/ScopInfo.h Fri Dec  2 02:10:56 2016
@@ -255,7 +255,9 @@ public:
   ///  @param Sizes       A vector of array sizes where the rightmost array
   ///                     sizes need to match the innermost array sizes already
   ///                     defined in SAI.
-  bool updateSizes(ArrayRef<const SCEV *> Sizes);
+  ///  @param CheckConsistency Update sizes, even if new sizes are inconsistent
+  ///                          with old sizes
+  bool updateSizes(ArrayRef<const SCEV *> Sizes, bool CheckConsistency = true);
 
   /// Destructor to free the isl id of the base pointer.
   ~ScopArrayInfo();
@@ -1002,6 +1004,15 @@ public:
   /// Get the reduction type of this access
   ReductionType getReductionType() const { return RedType; }
 
+  /// Update the original access relation.
+  ///
+  /// We need to update the original access relation during scop construction,
+  /// when unifying the memory accesses that access the same scop array info
+  /// object. After the scop has been constructed, the original access relation
+  /// should not be changed any more. Instead setNewAccessRelation should
+  /// be called.
+  void setAccessRelation(__isl_take isl_map *AccessRelation);
+
   /// Set the updated access relation read from JSCOP file.
   void setNewAccessRelation(__isl_take isl_map *NewAccessRelation);
 
@@ -1884,6 +1895,33 @@ private:
   /// the dimensionality of the underlying ScopArrayInfo object.
   void updateAccessDimensionality();
 
+  /// Fold size constants to the right.
+  ///
+  /// In case all memory accesses in a given dimension are multiplied with a
+  /// common constant, we can remove this constant from the individual access
+  /// functions and move it to the size of the memory access. We do this as this
+  /// increases the size of the innermost dimension, consequently widens the
+  /// valid range the array subscript in this dimension can evaluate to, and
+  /// as a result increases the likelyhood that our delinearization is
+  /// correct.
+  ///
+  /// Example:
+  ///
+  ///    A[][n]
+  ///    S[i,j] -> A[2i][2j+1]
+  ///    S[i,j] -> A[2i][2j]
+  ///
+  ///    =>
+  ///
+  ///    A[][2n]
+  ///    S[i,j] -> A[i][2j+1]
+  ///    S[i,j] -> A[i][2j]
+  ///
+  /// Constants in outer dimensions can arise when the elements of a parametric
+  /// multi-dimensional array are not elementar data types, but e.g.,
+  /// structures.
+  void foldSizeConstantsToRight();
+
   /// Fold memory accesses to handle parametric offset.
   ///
   /// As a post-processing step, we 'fold' memory accesses to parameteric

Modified: polly/trunk/lib/Analysis/ScopInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/Analysis/ScopInfo.cpp?rev=288489&r1=288488&r2=288489&view=diff
==============================================================================
--- polly/trunk/lib/Analysis/ScopInfo.cpp (original)
+++ polly/trunk/lib/Analysis/ScopInfo.cpp Fri Dec  2 02:10:56 2016
@@ -244,20 +244,23 @@ void ScopArrayInfo::updateElementType(Ty
   }
 }
 
-bool ScopArrayInfo::updateSizes(ArrayRef<const SCEV *> NewSizes) {
+bool ScopArrayInfo::updateSizes(ArrayRef<const SCEV *> NewSizes,
+                                bool CheckConsistency) {
   int SharedDims = std::min(NewSizes.size(), DimensionSizes.size());
   int ExtraDimsNew = NewSizes.size() - SharedDims;
   int ExtraDimsOld = DimensionSizes.size() - SharedDims;
 
-  for (int i = 0; i < SharedDims; i++) {
-    auto *NewSize = NewSizes[i + ExtraDimsNew];
-    auto *KnownSize = DimensionSizes[i + ExtraDimsOld];
-    if (NewSize && KnownSize && NewSize != KnownSize)
-      return false;
-  }
+  if (CheckConsistency) {
+    for (int i = 0; i < SharedDims; i++) {
+      auto *NewSize = NewSizes[i + ExtraDimsNew];
+      auto *KnownSize = DimensionSizes[i + ExtraDimsOld];
+      if (NewSize && KnownSize && NewSize != KnownSize)
+        return false;
+    }
 
-  if (DimensionSizes.size() >= NewSizes.size())
-    return true;
+    if (DimensionSizes.size() >= NewSizes.size())
+      return true;
+  }
 
   DimensionSizes.clear();
   DimensionSizes.insert(DimensionSizes.begin(), NewSizes.begin(),
@@ -1048,6 +1051,11 @@ bool MemoryAccess::isStrideOne(__isl_tak
   return isStrideX(Schedule, 1);
 }
 
+void MemoryAccess::setAccessRelation(__isl_take isl_map *NewAccess) {
+  isl_map_free(AccessRelation);
+  AccessRelation = NewAccess;
+}
+
 void MemoryAccess::setNewAccessRelation(__isl_take isl_map *NewAccess) {
   assert(NewAccess);
 
@@ -3095,8 +3103,137 @@ Scop::Scop(Region &R, ScalarEvolution &S
   buildContext();
 }
 
+void Scop::foldSizeConstantsToRight() {
+  isl_union_set *Accessed = isl_union_map_range(getAccesses());
+
+  for (auto Array : arrays()) {
+    if (Array->getNumberOfDimensions() <= 1)
+      continue;
+
+    isl_space *Space = Array->getSpace();
+
+    Space = isl_space_align_params(Space, isl_union_set_get_space(Accessed));
+
+    if (!isl_union_set_contains(Accessed, Space)) {
+      isl_space_free(Space);
+      continue;
+    }
+
+    isl_set *Elements = isl_union_set_extract_set(Accessed, Space);
+
+    isl_map *Transform =
+        isl_map_universe(isl_space_map_from_set(Array->getSpace()));
+
+    std::vector<int> Int;
+
+    int Dims = isl_set_dim(Elements, isl_dim_set);
+    for (int i = 0; i < Dims; i++) {
+      isl_set *DimOnly =
+          isl_set_project_out(isl_set_copy(Elements), isl_dim_set, 0, i);
+      DimOnly = isl_set_project_out(DimOnly, isl_dim_set, 1, Dims - i - 1);
+      DimOnly = isl_set_lower_bound_si(DimOnly, isl_dim_set, 0, 0);
+
+      isl_basic_set *DimHull = isl_set_affine_hull(DimOnly);
+
+      if (i == Dims - 1) {
+        Int.push_back(1);
+        Transform = isl_map_equate(Transform, isl_dim_in, i, isl_dim_out, i);
+        isl_basic_set_free(DimHull);
+        continue;
+      }
+
+      if (isl_basic_set_dim(DimHull, isl_dim_div) == 1) {
+        isl_aff *Diff = isl_basic_set_get_div(DimHull, 0);
+        isl_val *Val = isl_aff_get_denominator_val(Diff);
+        isl_aff_free(Diff);
+
+        int ValInt = 1;
+
+        if (isl_val_is_int(Val))
+          ValInt = isl_val_get_num_si(Val);
+        isl_val_free(Val);
+
+        Int.push_back(ValInt);
+
+        isl_constraint *C = isl_constraint_alloc_equality(
+            isl_local_space_from_space(isl_map_get_space(Transform)));
+        C = isl_constraint_set_coefficient_si(C, isl_dim_out, i, ValInt);
+        C = isl_constraint_set_coefficient_si(C, isl_dim_in, i, -1);
+        Transform = isl_map_add_constraint(Transform, C);
+        isl_basic_set_free(DimHull);
+        continue;
+      }
+
+      isl_basic_set *ZeroSet = isl_basic_set_copy(DimHull);
+      ZeroSet = isl_basic_set_fix_si(ZeroSet, isl_dim_set, 0, 0);
+
+      int ValInt = 1;
+      if (isl_basic_set_is_equal(ZeroSet, DimHull)) {
+        ValInt = 0;
+      }
+
+      Int.push_back(ValInt);
+      Transform = isl_map_equate(Transform, isl_dim_in, i, isl_dim_out, i);
+      isl_basic_set_free(DimHull);
+      isl_basic_set_free(ZeroSet);
+    }
+
+    isl_set *MappedElements = isl_map_domain(isl_map_copy(Transform));
+
+    if (!isl_set_is_subset(Elements, MappedElements)) {
+      isl_set_free(Elements);
+      isl_set_free(MappedElements);
+      isl_map_free(Transform);
+      continue;
+    }
+
+    isl_set_free(MappedElements);
+
+    bool CanFold = true;
+
+    if (Int[0] <= 1)
+      CanFold = false;
+
+    unsigned NumDims = Array->getNumberOfDimensions();
+    for (unsigned i = 1; i < NumDims - 1; i++)
+      if (Int[0] != Int[i] && Int[i])
+        CanFold = false;
+
+    if (!CanFold) {
+      isl_set_free(Elements);
+      isl_map_free(Transform);
+      continue;
+    }
+
+    isl_map_dump(Transform);
+
+    for (auto &Access : AccessFunctions)
+      if (Access->getScopArrayInfo() == Array)
+        Access->setAccessRelation(isl_map_apply_range(
+            Access->getAccessRelation(), isl_map_copy(Transform)));
+
+    isl_map_free(Transform);
+
+    std::vector<const SCEV *> Sizes;
+    for (unsigned i = 0; i < NumDims; i++) {
+      auto Size = Array->getDimensionSize(i);
+
+      if (i == NumDims - 1)
+        Size = SE->getMulExpr(Size, SE->getConstant(Size->getType(), Int[0]));
+      Sizes.push_back(Size);
+    }
+
+    Array->updateSizes(Sizes, false /* CheckConsistency */);
+
+    isl_set_free(Elements);
+  }
+  isl_union_set_free(Accessed);
+  return;
+}
+
 void Scop::finalizeAccesses() {
   updateAccessDimensionality();
+  foldSizeConstantsToRight();
   foldAccessRelations();
   assumeNoOutOfBounds();
 }

Modified: polly/trunk/test/Isl/CodeGen/invariant-load-dimension.ll
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/test/Isl/CodeGen/invariant-load-dimension.ll?rev=288489&r1=288488&r2=288489&view=diff
==============================================================================
--- polly/trunk/test/Isl/CodeGen/invariant-load-dimension.ll (original)
+++ polly/trunk/test/Isl/CodeGen/invariant-load-dimension.ll Fri Dec  2 02:10:56 2016
@@ -17,7 +17,7 @@ define void @test(%S* %cpi, i1 %b) {
 ; SCOPS-NEXT:            Execution Context: [l2, l1] -> {  : l2 > 0 }
 ; SCOPS-NEXT:    }
 ; SCOPS:         Arrays {
-; SCOPS-NEXT:        i32 MemRef_cpi[*][%l1]; // Element size 4
+; SCOPS-NEXT:        i32 MemRef_cpi[*][(10 * %l1)]; // Element size 4
 ; SCOPS-NEXT:    }
 
 ; FIXME: Figure out how to actually generate code for this loop.

Added: polly/trunk/test/ScopInfo/multidim_fold_constant_dim.ll
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/test/ScopInfo/multidim_fold_constant_dim.ll?rev=288489&view=auto
==============================================================================
--- polly/trunk/test/ScopInfo/multidim_fold_constant_dim.ll (added)
+++ polly/trunk/test/ScopInfo/multidim_fold_constant_dim.ll Fri Dec  2 02:10:56 2016
@@ -0,0 +1,93 @@
+; RUN: opt %loadPolly -polly-scops -analyze -S < %s | FileCheck %s
+;
+;    struct com {
+;      double Real;
+;      double Img;
+;    };
+;
+;    void foo(long n, struct com A[][n]) {
+;      for (long i = 0; i < 100; i++)
+;        for (long j = 0; j < 1000; j++)
+;          A[i][j].Real += A[i][j].Img;
+;    }
+;
+;    int main() {
+;      struct com A[100][1000];
+;      foo(1000, A);
+;    }
+
+; CHECK:      Arrays {
+; CHECK-NEXT:     double MemRef_A[*][(2 * %n)]; // Element size 8
+; CHECK-NEXT: }
+
+; CHECK: 	Stmt_for_body3
+; CHECK-NEXT:         Domain :=
+; CHECK-NEXT:             [n] -> { Stmt_for_body3[i0, i1] : 0 <= i0 <= 99 and 0 <= i1 <= 999 };
+; CHECK-NEXT:         Schedule :=
+; CHECK-NEXT:             [n] -> { Stmt_for_body3[i0, i1] -> [i0, i1] };
+; CHECK-NEXT:         ReadAccess :=	[Reduction Type: NONE] [Scalar: 0]
+; CHECK-NEXT:             [n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, 1 + 2i1] };
+; CHECK-NEXT:         ReadAccess :=	[Reduction Type: NONE] [Scalar: 0]
+; CHECK-NEXT:             [n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, 2i1] };
+; CHECK-NEXT:         MustWriteAccess :=	[Reduction Type: NONE] [Scalar: 0]
+; CHECK-NEXT:             [n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, 2i1] };
+
+source_filename = "/tmp/test.c"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+%struct.com = type { double, double }
+
+define void @foo(i64 %n, %struct.com* %A) {
+entry:
+  br label %for.cond
+
+for.cond:                                         ; preds = %for.inc7, %entry
+  %i.0 = phi i64 [ 0, %entry ], [ %inc8, %for.inc7 ]
+  %exitcond1 = icmp ne i64 %i.0, 100
+  br i1 %exitcond1, label %for.body, label %for.end9
+
+for.body:                                         ; preds = %for.cond
+  br label %for.cond1
+
+for.cond1:                                        ; preds = %for.inc, %for.body
+  %j.0 = phi i64 [ 0, %for.body ], [ %inc, %for.inc ]
+  %exitcond = icmp ne i64 %j.0, 1000
+  br i1 %exitcond, label %for.body3, label %for.end
+
+for.body3:                                        ; preds = %for.cond1
+  %tmp = mul nsw i64 %i.0, %n
+  %arrayidx = getelementptr inbounds %struct.com, %struct.com* %A, i64 %tmp
+  %arrayidx4 = getelementptr inbounds %struct.com, %struct.com* %arrayidx, i64 %j.0
+  %Img = getelementptr inbounds %struct.com, %struct.com* %arrayidx4, i64 0, i32 1
+  %tmp2 = load double, double* %Img, align 8
+  %tmp3 = mul nsw i64 %i.0, %n
+  %arrayidx5 = getelementptr inbounds %struct.com, %struct.com* %A, i64 %tmp3
+  %arrayidx6 = getelementptr inbounds %struct.com, %struct.com* %arrayidx5, i64 %j.0
+  %Real = getelementptr inbounds %struct.com, %struct.com* %arrayidx6, i64 0, i32 0
+  %tmp4 = load double, double* %Real, align 8
+  %add = fadd double %tmp4, %tmp2
+  store double %add, double* %Real, align 8
+  br label %for.inc
+
+for.inc:                                          ; preds = %for.body3
+  %inc = add nuw nsw i64 %j.0, 1
+  br label %for.cond1
+
+for.end:                                          ; preds = %for.cond1
+  br label %for.inc7
+
+for.inc7:                                         ; preds = %for.end
+  %inc8 = add nuw nsw i64 %i.0, 1
+  br label %for.cond
+
+for.end9:                                         ; preds = %for.cond
+  ret void
+}
+
+define i32 @main() {
+entry:
+  %A = alloca [100 x [1000 x %struct.com]], align 16
+  %tmp = getelementptr inbounds [100 x [1000 x %struct.com]], [100 x [1000 x %struct.com]]* %A, i64 0, i64 0, i64 0
+  call void @foo(i64 1000, %struct.com* nonnull %tmp)
+  ret i32 0
+}




More information about the llvm-commits mailing list