am 770c3a79: am 9ca96e70: Support for pass-by-value kernels.

* commit '770c3a79216e65f844cab398c3a4b689b875578f':
  Support for pass-by-value kernels.
diff --git a/slang_rs_export_foreach.cpp b/slang_rs_export_foreach.cpp
index 484ab46..5754b41 100644
--- a/slang_rs_export_foreach.cpp
+++ b/slang_rs_export_foreach.cpp
@@ -50,6 +50,7 @@
 
 }  // namespace
 
+
 // This function takes care of additional validation and construction of
 // parameters related to forEach_* reflection.
 bool RSExportForEach::validateAndConstructParams(
@@ -60,7 +61,6 @@
   clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics();
 
   numParams = FD->getNumParams();
-  slangAssert(numParams > 0);
 
   if (Context->getTargetAPI() < SLANG_JB_TARGET_API) {
     if (!isRootRSFunc(FD)) {
@@ -76,8 +76,19 @@
     }
   }
 
-  // Compute kernel functions are required to return a void type for now
-  if (FD->getResultType().getCanonicalType() != C.VoidTy) {
+  mResultType = FD->getResultType().getCanonicalType();
+  // Compute kernel functions are required to return a void type or
+  // be marked explicitly as a kernel. In the case of
+  // "__attribute__((kernel))", we handle validation differently.
+  if (FD->hasAttr<clang::KernelAttr>()) {
+    return validateAndConstructKernelParams(Context, FD);
+  }
+
+  // If numParams is 0, we already marked this as a graphics root().
+  slangAssert(numParams > 0);
+
+  // Compute kernel functions of this type are required to return a void type.
+  if (mResultType != C.VoidTy) {
     DiagEngine->Report(
       clang::FullSourceLoc(FD->getLocation(), DiagEngine->getSourceManager()),
       DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
@@ -216,6 +227,166 @@
   return valid;
 }
 
+
+bool RSExportForEach::validateAndConstructKernelParams(RSContext *Context,
+    const clang::FunctionDecl *FD) {
+  slangAssert(Context && FD);
+  bool valid = true;
+  clang::ASTContext &C = Context->getASTContext();
+  clang::DiagnosticsEngine *DiagEngine = Context->getDiagnostics();
+
+  if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) {
+    DiagEngine->Report(
+      clang::FullSourceLoc(FD->getLocation(),
+                           DiagEngine->getSourceManager()),
+      DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                  "Compute kernel %0() targeting SDK levels "
+                                  "%1-%2 may not use pass-by-value with "
+                                  "__attribute__((kernel))"))
+      << FD->getName() << SLANG_MINIMUM_TARGET_API
+      << (SLANG_JB_MR1_TARGET_API - 1);
+    return false;
+  }
+
+  // Denote that we are indeed a pass-by-value kernel.
+  mKernel = true;
+
+  if (mResultType != C.VoidTy) {
+    mReturn = true;
+  }
+
+  if (mResultType->isPointerType()) {
+    DiagEngine->Report(
+      clang::FullSourceLoc(FD->getTypeSpecStartLoc(),
+                           DiagEngine->getSourceManager()),
+      DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                  "Compute kernel %0() cannot return a "
+                                  "pointer type: '%1'"))
+      << FD->getName() << mResultType.getAsString();
+    valid = false;
+  }
+
+  // Validate remaining parameter types
+  // TODO(all): Add support for LOD/face when we have them
+
+  size_t i = 0;
+  const clang::ParmVarDecl *PVD = NULL;
+  clang::QualType QT;
+
+  if (i < numParams) {
+    PVD = FD->getParamDecl(i);
+    QT = PVD->getType().getCanonicalType();
+
+    if (QT->isPointerType()) {
+      DiagEngine->Report(
+        clang::FullSourceLoc(PVD->getLocation(),
+                             DiagEngine->getSourceManager()),
+        DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                    "Compute kernel %0() cannot have "
+                                    "parameter '%1' of pointer type: '%2'"))
+        << FD->getName() << PVD->getName() << PVD->getType().getAsString();
+      valid = false;
+    } else if (QT.getUnqualifiedType() == C.UnsignedIntTy) {
+      // First parameter is either input or x, y (iff it is uint32_t).
+      llvm::StringRef ParamName = PVD->getName();
+      if (ParamName.equals("x")) {
+        mX = PVD;
+      } else if (ParamName.equals("y")) {
+        mY = PVD;
+      } else {
+        mIn = PVD;
+      }
+    } else {
+      mIn = PVD;
+    }
+
+    i++;  // advance parameter pointer
+  }
+
+  // Check that we have at least one allocation to use for dimensions.
+  if (valid && !mIn && !mReturn) {
+    DiagEngine->Report(
+      clang::FullSourceLoc(FD->getLocation(),
+                           DiagEngine->getSourceManager()),
+      DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                  "Compute kernel %0() must have at least one "
+                                  "input parameter or a non-void return "
+                                  "type")) << FD->getName();
+    valid = false;
+  }
+
+  // TODO: Abstract this block away, since it is duplicate code.
+  while (i < numParams) {
+    PVD = FD->getParamDecl(i);
+    QT = PVD->getType().getCanonicalType();
+
+    if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
+      DiagEngine->Report(
+        clang::FullSourceLoc(PVD->getLocation(),
+                             DiagEngine->getSourceManager()),
+        DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                    "Unexpected kernel %0() parameter '%1' "
+                                    "of type '%2'"))
+        << FD->getName() << PVD->getName() << PVD->getType().getAsString();
+      valid = false;
+    } else {
+      llvm::StringRef ParamName = PVD->getName();
+      if (ParamName.equals("x")) {
+        if (mX) {
+          ReportNameError(DiagEngine, PVD);
+          valid = false;
+        } else if (mY) {
+          // Can't go back to X after skipping Y
+          ReportNameError(DiagEngine, PVD);
+          valid = false;
+        } else {
+          mX = PVD;
+        }
+      } else if (ParamName.equals("y")) {
+        if (mY) {
+          ReportNameError(DiagEngine, PVD);
+          valid = false;
+        } else {
+          mY = PVD;
+        }
+      } else {
+        if (!mX && !mY) {
+          mX = PVD;
+        } else if (!mY) {
+          mY = PVD;
+        } else {
+          DiagEngine->Report(
+            clang::FullSourceLoc(PVD->getLocation(),
+                                 DiagEngine->getSourceManager()),
+            DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                        "Unexpected kernel %0() parameter '%1' "
+                                        "of type '%2'"))
+            << FD->getName() << PVD->getName() << PVD->getType().getAsString();
+          valid = false;
+        }
+      }
+    }
+
+    i++;  // advance parameter pointer
+  }
+
+  mSignatureMetadata = 0;
+  if (valid) {
+    // Set up the bitwise metadata encoding for runtime argument passing.
+    mSignatureMetadata |= (mIn ?       0x01 : 0);
+    slangAssert(mOut == NULL);
+    mSignatureMetadata |= (mReturn ?   0x02 : 0);
+    slangAssert(mUsrData == NULL);
+    mSignatureMetadata |= (mUsrData ?  0x04 : 0);
+    mSignatureMetadata |= (mX ?        0x08 : 0);
+    mSignatureMetadata |= (mY ?        0x10 : 0);
+    mSignatureMetadata |= (mKernel ?   0x20 : 0);  // pass-by-value
+  }
+
+  return valid;
+}
+
+
 RSExportForEach *RSExportForEach::Create(RSContext *Context,
                                          const clang::FunctionDecl *FD) {
   slangAssert(Context && FD);
@@ -293,9 +464,16 @@
   if (FE->mIn) {
     const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr();
     FE->mInType = RSExportType::Create(Context, T);
+    if (FE->mKernel) {
+      slangAssert(FE->mInType);
+    }
   }
 
-  if (FE->mOut) {
+  if (FE->mKernel && FE->mReturn) {
+    const clang::Type *T = FE->mResultType.getTypePtr();
+    FE->mOutType = RSExportType::Create(Context, T);
+    slangAssert(FE->mOutType);
+  } else if (FE->mOut) {
     const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr();
     FE->mOutType = RSExportType::Create(Context, T);
   }
@@ -313,6 +491,10 @@
 
 bool RSExportForEach::isGraphicsRootRSFunc(int targetAPI,
                                            const clang::FunctionDecl *FD) {
+  if (FD->hasAttr<clang::KernelAttr>()) {
+    return false;
+  }
+
   if (!isRootRSFunc(FD)) {
     return false;
   }
@@ -335,6 +517,11 @@
 
 bool RSExportForEach::isRSForEachFunc(int targetAPI,
     const clang::FunctionDecl *FD) {
+  // Anything tagged as a kernel is definitely used with ForEach.
+  if (FD->hasAttr<clang::KernelAttr>()) {
+    return true;
+  }
+
   if (isGraphicsRootRSFunc(targetAPI, FD)) {
     return false;
   }
diff --git a/slang_rs_export_foreach.h b/slang_rs_export_foreach.h
index c8b1dd0..9bc185e 100644
--- a/slang_rs_export_foreach.h
+++ b/slang_rs_export_foreach.h
@@ -53,6 +53,10 @@
   const clang::ParmVarDecl *mZ;
   const clang::ParmVarDecl *mAr;
 
+  clang::QualType mResultType;  // return type (if present).
+  bool mReturn;  // does this kernel have a return type?
+  bool mKernel;  // is this a pass-by-value kernel?
+
   bool mDummyRoot;
 
   // TODO(all): Add support for LOD/face when we have them
@@ -60,14 +64,18 @@
     : RSExportable(Context, RSExportable::EX_FOREACH),
       mName(Name.data(), Name.size()), mParamPacketType(NULL), mInType(NULL),
       mOutType(NULL), numParams(0), mSignatureMetadata(0),
-      mIn(NULL), mOut(NULL), mUsrData(NULL),
-      mX(NULL), mY(NULL), mZ(NULL), mAr(NULL), mDummyRoot(false) {
+      mIn(NULL), mOut(NULL), mUsrData(NULL), mX(NULL), mY(NULL), mZ(NULL),
+      mAr(NULL), mResultType(clang::QualType()), mReturn(false),
+      mKernel(false), mDummyRoot(false) {
     return;
   }
 
   bool validateAndConstructParams(RSContext *Context,
                                   const clang::FunctionDecl *FD);
 
+  bool validateAndConstructKernelParams(RSContext *Context,
+                                        const clang::FunctionDecl *FD);
+
  public:
   static RSExportForEach *Create(RSContext *Context,
                                  const clang::FunctionDecl *FD);
@@ -94,6 +102,10 @@
     return (mUsrData != NULL);
   }
 
+  inline bool hasReturn() const {
+    return mReturn;
+  }
+
   inline const RSExportType *getInType() const {
     return mInType;
   }
diff --git a/slang_rs_reflection.cpp b/slang_rs_reflection.cpp
index c781241..cd088a4 100644
--- a/slang_rs_reflection.cpp
+++ b/slang_rs_reflection.cpp
@@ -648,11 +648,11 @@
   // forEach_*()
   Context::ArgTy Args;
 
-  slangAssert(EF->getNumParameters() > 0);
+  slangAssert(EF->getNumParameters() > 0 || EF->hasReturn());
 
   if (EF->hasIn())
     Args.push_back(std::make_pair("Allocation", "ain"));
-  if (EF->hasOut())
+  if (EF->hasOut() || EF->hasReturn())
     Args.push_back(std::make_pair("Allocation", "aout"));
 
   const RSExportRecordType *ERT = EF->getParamPacketType();
@@ -682,7 +682,7 @@
     genTypeCheck(C, OET, "aout");
   }
 
-  if (EF->hasIn() && EF->hasOut()) {
+  if (EF->hasIn() && (EF->hasOut() || EF->hasReturn())) {
     C.indent() << "// Verify dimensions" << std::endl;
     C.indent() << "Type tIn = ain.getType();" << std::endl;
     C.indent() << "Type tOut = aout.getType();" << std::endl;
@@ -711,7 +711,7 @@
   else
     C.out() << ", null";
 
-  if (EF->hasOut())
+  if (EF->hasOut() || EF->hasReturn())
     C.out() << ", aout";
   else
     C.out() << ", null";
@@ -730,9 +730,13 @@
 void RSReflection::genTypeInstanceFromPointer(Context &C,
                                               const RSExportType *ET) {
   if (ET->getClass() == RSExportType::ExportClassPointer) {
+    // For pointer parameters to original forEach kernels.
     const RSExportPointerType *EPT =
         static_cast<const RSExportPointerType*>(ET);
     genTypeInstance(C, EPT->getPointeeType());
+  } else {
+    // For handling pass-by-value kernel parameters.
+    genTypeInstance(C, ET);
   }
 }
 
diff --git a/tests/F_kernel_16/kernel_16.rs b/tests/F_kernel_16/kernel_16.rs
new file mode 100644
index 0000000..56007a2
--- /dev/null
+++ b/tests/F_kernel_16/kernel_16.rs
@@ -0,0 +1,7 @@
+// -target-api 16
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void __attribute__((kernel)) root(int i) {
+}
+
diff --git a/tests/F_kernel_16/stderr.txt.expect b/tests/F_kernel_16/stderr.txt.expect
new file mode 100644
index 0000000..b8a96ec
--- /dev/null
+++ b/tests/F_kernel_16/stderr.txt.expect
@@ -0,0 +1 @@
+kernel_16.rs:5:30: error: Compute kernel root() targeting SDK levels 11-16 may not use pass-by-value with __attribute__((kernel))
diff --git a/tests/F_kernel_16/stdout.txt.expect b/tests/F_kernel_16/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_kernel_16/stdout.txt.expect
diff --git a/tests/F_kernel_badsig/kernel_badsig.rs b/tests/F_kernel_badsig/kernel_badsig.rs
new file mode 100644
index 0000000..db7652d
--- /dev/null
+++ b/tests/F_kernel_badsig/kernel_badsig.rs
@@ -0,0 +1,6 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void __attribute__((kernel)) root(uint32_t x) {
+}
+
diff --git a/tests/F_kernel_badsig/stderr.txt.expect b/tests/F_kernel_badsig/stderr.txt.expect
new file mode 100644
index 0000000..9f2b7ac
--- /dev/null
+++ b/tests/F_kernel_badsig/stderr.txt.expect
@@ -0,0 +1 @@
+kernel_badsig.rs:4:30: error: Compute kernel root() must have at least one input parameter or a non-void return type
diff --git a/tests/F_kernel_badsig/stdout.txt.expect b/tests/F_kernel_badsig/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_kernel_badsig/stdout.txt.expect
diff --git a/tests/F_kernel_noattr/kernel_noattr.rs b/tests/F_kernel_noattr/kernel_noattr.rs
new file mode 100644
index 0000000..e46888b
--- /dev/null
+++ b/tests/F_kernel_noattr/kernel_noattr.rs
@@ -0,0 +1,18 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int root(uint32_t ain) {
+  return 0;
+}
+
+void in_only(uint32_t ain) {
+}
+
+int out_only() {
+  return 0;
+}
+
+int everything(uint32_t ain, uint32_t x, uint32_t y) {
+  return 0;
+}
+
diff --git a/tests/F_kernel_noattr/stderr.txt.expect b/tests/F_kernel_noattr/stderr.txt.expect
new file mode 100644
index 0000000..d8e054f
--- /dev/null
+++ b/tests/F_kernel_noattr/stderr.txt.expect
@@ -0,0 +1,4 @@
+kernel_noattr.rs:4:5: error: Compute kernel root() is required to return a void type
+kernel_noattr.rs:4:5: error: Compute kernel root() must have at least one parameter for in or out
+kernel_noattr.rs:11:5: error: invokable non-static functions are required to return void
+kernel_noattr.rs:15:5: error: invokable non-static functions are required to return void
diff --git a/tests/F_kernel_noattr/stdout.txt.expect b/tests/F_kernel_noattr/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_kernel_noattr/stdout.txt.expect
diff --git a/tests/F_kernel_ptr_param/kernel_ptr_param.rs b/tests/F_kernel_ptr_param/kernel_ptr_param.rs
new file mode 100644
index 0000000..2467793
--- /dev/null
+++ b/tests/F_kernel_ptr_param/kernel_ptr_param.rs
@@ -0,0 +1,6 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void __attribute__((kernel)) root(int *i) {
+}
+
diff --git a/tests/F_kernel_ptr_param/stderr.txt.expect b/tests/F_kernel_ptr_param/stderr.txt.expect
new file mode 100644
index 0000000..36f864c
--- /dev/null
+++ b/tests/F_kernel_ptr_param/stderr.txt.expect
@@ -0,0 +1 @@
+kernel_ptr_param.rs:4:40: error: Compute kernel root() cannot have parameter 'i' of pointer type: 'int *'
diff --git a/tests/F_kernel_ptr_param/stdout.txt.expect b/tests/F_kernel_ptr_param/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_kernel_ptr_param/stdout.txt.expect
diff --git a/tests/F_kernel_ptr_ret_val/kernel_ptr_ret_val.rs b/tests/F_kernel_ptr_ret_val/kernel_ptr_ret_val.rs
new file mode 100644
index 0000000..f964b31
--- /dev/null
+++ b/tests/F_kernel_ptr_ret_val/kernel_ptr_ret_val.rs
@@ -0,0 +1,7 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int * __attribute__((kernel)) root() {
+  return NULL;
+}
+
diff --git a/tests/F_kernel_ptr_ret_val/stderr.txt.expect b/tests/F_kernel_ptr_ret_val/stderr.txt.expect
new file mode 100644
index 0000000..7567c1d
--- /dev/null
+++ b/tests/F_kernel_ptr_ret_val/stderr.txt.expect
@@ -0,0 +1 @@
+kernel_ptr_ret_val.rs:4:1: error: Compute kernel root() cannot return a pointer type: 'int *'
diff --git a/tests/F_kernel_ptr_ret_val/stdout.txt.expect b/tests/F_kernel_ptr_ret_val/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_kernel_ptr_ret_val/stdout.txt.expect
diff --git a/tests/P_kernel/kernel.rs b/tests/P_kernel/kernel.rs
new file mode 100644
index 0000000..d8f0c4d
--- /dev/null
+++ b/tests/P_kernel/kernel.rs
@@ -0,0 +1,18 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel)) root(uint32_t ain) {
+  return 0;
+}
+
+void __attribute__((kernel)) in_only(uint32_t ain) {
+}
+
+int __attribute__((kernel)) out_only() {
+  return 0;
+}
+
+int __attribute__((kernel)) everything(uint32_t ain, uint32_t x, uint32_t y) {
+  return 0;
+}
+
diff --git a/tests/P_kernel/stderr.txt.expect b/tests/P_kernel/stderr.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_kernel/stderr.txt.expect
diff --git a/tests/P_kernel/stdout.txt.expect b/tests/P_kernel/stdout.txt.expect
new file mode 100644
index 0000000..a58134c
--- /dev/null
+++ b/tests/P_kernel/stdout.txt.expect
@@ -0,0 +1 @@
+Generating ScriptC_kernel.java ...