CodeGen: Add scop-parameters to the OpenMP context
authorTobias Grosser <grosser@fim.uni-passau.de>
Thu, 1 Nov 2012 05:34:48 +0000 (05:34 +0000)
committerTobias Grosser <grosser@fim.uni-passau.de>
Thu, 1 Nov 2012 05:34:48 +0000 (05:34 +0000)
In addition to the arrays and clast variables a SCoP statement may also refer to
values defined before the SCoP or to function arguments. Detect these values and
add them to the set of values passed to the function generated for OpenMP
parallel execution of a clast.

Committed with additional test cases and some refactoring.

Contributed by:  Armin Groesslinger  <armin.groesslinger@uni-passau.de>

llvm-svn: 167214

polly/include/polly/CodeGen/Cloog.h
polly/lib/CodeGen/Cloog.cpp
polly/lib/CodeGen/CodeGeneration.cpp
polly/test/CodeGen/OpenMP/copy_in_argument.ll [new file with mode: 0644]
polly/test/CodeGen/OpenMP/copy_in_temporary.ll [new file with mode: 0644]
polly/test/CodeGen/OpenMP/param_referenced_in_stmt.ll [new file with mode: 0644]

index de08516..22ddea2 100644 (file)
@@ -64,6 +64,21 @@ namespace polly {
     virtual void getAnalysisUsage(AnalysisUsage &AU) const;
     virtual void releaseMemory();
   };
+
+  // Visitor class for clasts.
+  // Only 'visitUser' has to be implemented by subclasses; the default
+  // implementations of the other methods traverse the clast recursively.
+  class ClastVisitor {
+  public:
+    virtual void visit(const clast_stmt *stmt);
+
+    virtual void visitAssignment(const clast_assignment *stmt);
+    virtual void visitBlock(const clast_block *stmt);
+    virtual void visitFor(const clast_for *stmt);
+    virtual void visitGuard(const clast_guard *stmt);
+
+    virtual void visitUser(const clast_user_stmt *stmt) = 0;
+  };
 }
 
 namespace llvm {
index bdfa8be..f08aa1c 100644 (file)
@@ -217,6 +217,40 @@ CloogInput *Cloog::buildCloogInput() {
   CloogInput *Input = cloog_input_alloc(Context, Statements);
   return Input;
 }
+
+void ClastVisitor::visit(const clast_stmt *stmt) {
+  if      (CLAST_STMT_IS_A(stmt, stmt_root))
+    assert(false && "No second root statement expected");
+  else if (CLAST_STMT_IS_A(stmt, stmt_ass))
+    return visitAssignment((const clast_assignment *)stmt);
+  else if (CLAST_STMT_IS_A(stmt, stmt_user))
+    return visitUser((const clast_user_stmt *)stmt);
+  else if (CLAST_STMT_IS_A(stmt, stmt_block))
+    return visitBlock((const clast_block *)stmt);
+  else if (CLAST_STMT_IS_A(stmt, stmt_for))
+    return visitFor((const clast_for *)stmt);
+  else if (CLAST_STMT_IS_A(stmt, stmt_guard))
+    return visitGuard((const clast_guard *)stmt);
+
+  if (stmt->next)
+    visit(stmt->next);
+}
+
+void ClastVisitor::visitAssignment(const clast_assignment *stmt) {
+}
+
+void ClastVisitor::visitBlock(const clast_block *stmt) {
+  visit(stmt->body);
+}
+
+void ClastVisitor::visitFor(const clast_for *stmt) {
+  visit(stmt->body);
+}
+
+void ClastVisitor::visitGuard(const clast_guard *stmt) {
+  visit(stmt->then);
+}
+
 } // End namespace polly.
 
 namespace {
index d713ae4..bac2427 100644 (file)
@@ -283,7 +283,7 @@ private:
   ///
   /// Create a list of values that has to be stored into the OpenMP subfuncition
   /// structure.
-  SetVector<Value*> getOMPValues();
+  SetVector<Value*> getOMPValues(const clast_stmt *Body);
 
   /// @brief Update ClastVars and ValueMap according to a value map.
   ///
@@ -461,7 +461,42 @@ void ClastStmtCodeGen::codegenForSequential(const clast_for *f) {
   Builder.SetInsertPoint(AfterBB->begin());
 }
 
-SetVector<Value*> ClastStmtCodeGen::getOMPValues() {
+// Helper class to determine all scalar parameters used in the basic blocks of a
+// clast. Scalar parameters are scalar variables defined outside of the SCoP.
+class ParameterVisitor : public ClastVisitor {
+  std::set<Value *> Values;
+public:
+  ParameterVisitor() : ClastVisitor(), Values() { }
+
+  void visitUser(const clast_user_stmt *Stmt) {
+    const ScopStmt *S = static_cast<const ScopStmt *>(Stmt->statement->usr);
+    const BasicBlock *BB = S->getBasicBlock();
+
+    // Check all the operands of instructions in the basic block.
+    for (BasicBlock::const_iterator BI = BB->begin(), BE = BB->end(); BI != BE;
+         ++BI) {
+      const Instruction &Inst = *BI;
+      for (Instruction::const_op_iterator II = Inst.op_begin(),
+           IE = Inst.op_end(); II != IE; ++II) {
+        Value *SrcVal = *II;
+
+        if (Instruction *OpInst = dyn_cast<Instruction>(SrcVal))
+          if (S->getParent()->getRegion().contains(OpInst))
+            continue;
+
+        if (isa<Instruction>(SrcVal) || isa<Argument>(SrcVal))
+          Values.insert(SrcVal);
+      }
+    }
+  }
+
+  // Iterator to iterate over the values found.
+  typedef std::set<Value *>::const_iterator const_iterator;
+  inline const_iterator begin() const { return Values.begin(); }
+  inline const_iterator end()   const { return Values.end();   }
+};
+
+SetVector<Value*> ClastStmtCodeGen::getOMPValues(const clast_stmt *Body) {
   SetVector<Value*> Values;
 
   // The clast variables
@@ -479,6 +514,20 @@ SetVector<Value*> ClastStmtCodeGen::getOMPValues() {
     }
   }
 
+  // Find the temporaries that are referenced in the clast statements'
+  // basic blocks but are not defined by these blocks (e.g., references
+  // to function arguments or temporaries defined before the start of
+  // the SCoP).
+  ParameterVisitor Params;
+  Params.visit(Body);
+
+  for (ParameterVisitor::const_iterator PI = Params.begin(), PE = Params.end();
+       PI != PE; ++PI) {
+    Value *V = *PI;
+    Values.insert(V);
+    DEBUG(dbgs() << "Adding temporary for OMP copy-in: " << *V << "\n");
+  }
+
   return Values;
 }
 
@@ -525,7 +574,7 @@ void ClastStmtCodeGen::codegenForOpenMP(const clast_for *For) {
   LB = ExpGen.codegen(For->LB, IntPtrTy);
   UB = ExpGen.codegen(For->UB, IntPtrTy);
 
-  Values = getOMPValues();
+  Values = getOMPValues(For->body);
 
   IV = OMPGen.createParallelLoop(LB, UB, Stride, Values, VMap, &LoopBody);
   BasicBlock::iterator AfterLoop = Builder.GetInsertPoint();
diff --git a/polly/test/CodeGen/OpenMP/copy_in_argument.ll b/polly/test/CodeGen/OpenMP/copy_in_argument.ll
new file mode 100644 (file)
index 0000000..cc45cf6
--- /dev/null
@@ -0,0 +1,34 @@
+; RUN: opt %loadPolly -polly-codegen -enable-polly-openmp -S %s | FileCheck %s
+;
+; 'arg' has the same type as A[i], i.e., the function argument has to be
+; copied to the function generated for the loop.
+;
+; float A[100];
+; void copy_in_test(float arg) {
+;     long i;
+;     for (i=0; i<100; ++i)
+;         A[i] = arg;
+; }
+
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@A = common global [100 x float] zeroinitializer, align 16
+
+define void @copy_in_test(float %arg) nounwind uwtable {
+entry:
+  br label %for.body
+
+for.body:                                         ; preds = %for.body, %entry
+  %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]
+  %arrayidx = getelementptr inbounds [100 x float]* @A, i64 0, i64 %indvars.iv
+  store float %arg, float* %arrayidx
+  %indvars.iv.next = add i64 %indvars.iv, 1
+  %exitcond = icmp eq i64 %indvars.iv.next, 100
+  br i1 %exitcond, label %for.end, label %for.body
+
+for.end:                                          ; preds = %for.body
+  ret void
+}
+
+; CHECK: %omp.userContext = alloca { [100 x float]*, float }
diff --git a/polly/test/CodeGen/OpenMP/copy_in_temporary.ll b/polly/test/CodeGen/OpenMP/copy_in_temporary.ll
new file mode 100644 (file)
index 0000000..1bd2352
--- /dev/null
@@ -0,0 +1,35 @@
+; RUN: opt %loadPolly -polly-codegen -enable-polly-openmp -S %s | FileCheck %s
+;
+; 'arg' is converted to float before the loop, so the corresponding temporary
+; has to be copied to the function generated for the loop.
+;
+; float A[100];
+; void copy_in_test(long arg) {
+;   long i;
+;   for (i=0; i<100; ++i)
+;     A[i] = arg;
+; }
+
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@A = common global [100 x float] zeroinitializer, align 16
+
+define void @copy_in_test(i64 %arg) nounwind uwtable {
+entry:
+  %conv = sitofp i64 %arg to float
+  br label %for.body
+
+for.body:                                         ; preds = %for.body, %entry
+  %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]
+  %arrayidx = getelementptr inbounds [100 x float]* @A, i64 0, i64 %indvars.iv
+  store float %conv, float* %arrayidx
+  %indvars.iv.next = add i64 %indvars.iv, 1
+  %exitcond = icmp eq i64 %indvars.iv.next, 100
+  br i1 %exitcond, label %for.end, label %for.body
+
+for.end:                                          ; preds = %for.body
+  ret void
+}
+
+; CHECK: %omp.userContext = alloca { [100 x float]*, float }
diff --git a/polly/test/CodeGen/OpenMP/param_referenced_in_stmt.ll b/polly/test/CodeGen/OpenMP/param_referenced_in_stmt.ll
new file mode 100644 (file)
index 0000000..f7c2887
--- /dev/null
@@ -0,0 +1,38 @@
+; RUN: opt %loadPolly -polly-codegen %s -enable-polly-openmp -S | FileCheck %s
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+
+; This test case implements the following code:
+;
+; for (i = 0; i < 1024; i++)
+;   A[i] = A[i] * param
+;
+; The problem is that 'param' is not references in any subscript of loop
+; bound, but it must still be forwarded to the OpenMP subfunction.
+
+define void @foo(double %param, [1024 x double]* %A) {
+entry:
+  br label %for.preheader
+
+for.preheader:
+  br label %for.body
+
+for.body:
+  %indvar = phi i64 [ 0, %for.preheader ], [ %indvar.next, %for.inc ]
+  %arrayidx = getelementptr [1024 x double]* %A, i64 0, i64 %indvar
+  %val = load double* %arrayidx
+  %mul = fmul double %param, %val
+  store double %mul, double* %arrayidx, align 8
+  br label %for.inc
+
+for.inc:
+  %indvar.next = add i64 %indvar, 1
+  %exitcond = icmp eq i64 %indvar.next, 1024
+  br i1 %exitcond, label %for.end, label %for.body
+
+for.end:
+  ret void
+}
+
+; CHECK: omp_subfn