Add initial support for validating and exporting reduce kernels.

Bug: 22631253

This change adds support to slang to validate the reduce-style
kernels for errors, and also to create reduce metadata.

Change-Id: Ic9144402dff93a2a28687864637e67fca6808e2e
diff --git a/Android.mk b/Android.mk
index 4fdd5a5..091a852 100644
--- a/Android.mk
+++ b/Android.mk
@@ -160,7 +160,8 @@
 	slang_rs_export_element.cpp	\
 	slang_rs_export_var.cpp	\
 	slang_rs_export_func.cpp	\
-	slang_rs_export_foreach.cpp \
+	slang_rs_export_foreach.cpp	\
+	slang_rs_export_reduce.cpp	\
 	slang_rs_object_ref_count.cpp	\
 	slang_rs_reflection.cpp \
 	slang_rs_reflection_cpp.cpp \
diff --git a/lit-tests/reduce_metadata/reduce.rs b/lit-tests/reduce_metadata/reduce.rs
new file mode 100644
index 0000000..82da76d
--- /dev/null
+++ b/lit-tests/reduce_metadata/reduce.rs
@@ -0,0 +1,349 @@
+// Check for generation of reduce metadata.
+
+// RUN: %Slang -target-api 0 %s
+// RUN: %rs-filecheck-wrapper %s
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+// CHECK-NOT: foreach
+// CHECK: !\23rs_export_reduce =
+// CHECK-NOT: foreach
+
+// CHECK: !{!"mul_bool"}
+bool __attribute__((kernel("reduce")))
+mul_bool(bool lhs, bool rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_char"}
+char __attribute__((kernel("reduce")))
+mul_char(char lhs, char rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_char2"}
+char2 __attribute__((kernel("reduce")))
+mul_char2(char2 lhs, char2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_char3"}
+char3 __attribute__((kernel("reduce")))
+mul_char3(char3 lhs, char3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_char4"}
+char4 __attribute__((kernel("reduce")))
+mul_char4(char4 lhs, char4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_double"}
+double __attribute__((kernel("reduce")))
+mul_double(double lhs, double rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_double2"}
+double2 __attribute__((kernel("reduce")))
+mul_double2(double2 lhs, double2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_double3"}
+double3 __attribute__((kernel("reduce")))
+mul_double3(double3 lhs, double3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_double4"}
+double4 __attribute__((kernel("reduce")))
+mul_double4(double4 lhs, double4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_float"}
+float __attribute__((kernel("reduce")))
+mul_float(float lhs, float rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_float2"}
+float2 __attribute__((kernel("reduce")))
+mul_float2(float2 lhs, float2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_float3"}
+float3 __attribute__((kernel("reduce")))
+mul_float3(float3 lhs, float3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_float4"}
+float4 __attribute__((kernel("reduce")))
+mul_float4(float4 lhs, float4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_int"}
+int __attribute__((kernel("reduce")))
+mul_int(int lhs, int rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_int2"}
+int2 __attribute__((kernel("reduce")))
+mul_int2(int2 lhs, int2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_int3"}
+int3 __attribute__((kernel("reduce")))
+mul_int3(int3 lhs, int3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_int4"}
+int4 __attribute__((kernel("reduce")))
+mul_int4(int4 lhs, int4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_long"}
+long __attribute__((kernel("reduce")))
+mul_long(long lhs, long rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_long2"}
+long2 __attribute__((kernel("reduce")))
+mul_long2(long2 lhs, long2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_long3"}
+long3 __attribute__((kernel("reduce")))
+mul_long3(long3 lhs, long3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_long4"}
+long4 __attribute__((kernel("reduce")))
+mul_long4(long4 lhs, long4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_short"}
+short __attribute__((kernel("reduce")))
+mul_short(short lhs, short rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_short2"}
+short2 __attribute__((kernel("reduce")))
+mul_short2(short2 lhs, short2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_short3"}
+short3 __attribute__((kernel("reduce")))
+mul_short3(short3 lhs, short3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_short4"}
+short4 __attribute__((kernel("reduce")))
+mul_short4(short4 lhs, short4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uchar"}
+uchar __attribute__((kernel("reduce")))
+mul_uchar(uchar lhs, uchar rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uchar2"}
+uchar2 __attribute__((kernel("reduce")))
+mul_uchar2(uchar2 lhs, uchar2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uchar3"}
+uchar3 __attribute__((kernel("reduce")))
+mul_uchar3(uchar3 lhs, uchar3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uchar4"}
+uchar4 __attribute__((kernel("reduce")))
+mul_uchar4(uchar4 lhs, uchar4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uint"}
+uint __attribute__((kernel("reduce")))
+mul_uint(uint lhs, uint rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uint2"}
+uint2 __attribute__((kernel("reduce")))
+mul_uint2(uint2 lhs, uint2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uint3"}
+uint3 __attribute__((kernel("reduce")))
+mul_uint3(uint3 lhs, uint3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_uint4"}
+uint4 __attribute__((kernel("reduce")))
+mul_uint4(uint4 lhs, uint4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ulong"}
+ulong __attribute__((kernel("reduce")))
+mul_ulong(ulong lhs, ulong rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ulong2"}
+ulong2 __attribute__((kernel("reduce")))
+mul_ulong2(ulong2 lhs, ulong2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ulong3"}
+ulong3 __attribute__((kernel("reduce")))
+mul_ulong3(ulong3 lhs, ulong3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ulong4"}
+ulong4 __attribute__((kernel("reduce")))
+mul_ulong4(ulong4 lhs, ulong4 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ushort"}
+ushort __attribute__((kernel("reduce")))
+mul_ushort(ushort lhs, ushort rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ushort2"}
+ushort2 __attribute__((kernel("reduce")))
+mul_ushort2(ushort2 lhs, ushort2 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ushort3"}
+ushort3 __attribute__((kernel("reduce")))
+mul_ushort3(ushort3 lhs, ushort3 rhs) {
+  return lhs * rhs;
+}
+
+// CHECK: !{!"mul_ushort4"}
+ushort4 __attribute__((kernel("reduce")))
+mul_ushort4(ushort4 lhs, ushort4 rhs) {
+  return lhs * rhs;
+}
+
+
+struct indirect {
+  bool elem_bool;
+  char elem_char;
+  char2 elem_char2;
+  char3 elem_char3;
+  char4 elem_char4;
+  double elem_double;
+  double2 elem_double2;
+  double3 elem_double3;
+  double4 elem_double4;
+  float elem_float;
+  float2 elem_float2;
+  float3 elem_float3;
+  float4 elem_float4;
+  int elem_int;
+  int2 elem_int2;
+  int3 elem_int3;
+  int4 elem_int4;
+  long elem_long;
+  long2 elem_long2;
+  long3 elem_long3;
+  long4 elem_long4;
+  short elem_short;
+  short2 elem_short2;
+  short3 elem_short3;
+  short4 elem_short4;
+  uchar elem_uchar;
+  uchar2 elem_uchar2;
+  uchar3 elem_uchar3;
+  uchar4 elem_uchar4;
+  uint elem_uint;
+  uint2 elem_uint2;
+  uint3 elem_uint3;
+  uint4 elem_uint4;
+  ulong elem_ulong;
+  ulong2 elem_ulong2;
+  ulong3 elem_ulong3;
+  ulong4 elem_ulong4;
+  ushort elem_ushort;
+  ushort2 elem_ushort2;
+  ushort3 elem_ushort3;
+  ushort4 elem_ushort4;
+};
+
+// CHECK: !{!"mul_indirect"}
+struct indirect __attribute__((kernel("reduce")))
+mul_indirect(struct indirect lhs, struct indirect rhs) {
+  lhs.elem_bool *= rhs.elem_bool;
+  lhs.elem_char *= rhs.elem_char;
+  lhs.elem_char2 *= rhs.elem_char2;
+  lhs.elem_char3 *= rhs.elem_char3;
+  lhs.elem_char4 *= rhs.elem_char4;
+  lhs.elem_double *= rhs.elem_double;
+  lhs.elem_double2 *= rhs.elem_double2;
+  lhs.elem_double3 *= rhs.elem_double3;
+  lhs.elem_double4 *= rhs.elem_double4;
+  lhs.elem_float *= rhs.elem_float;
+  lhs.elem_float2 *= rhs.elem_float2;
+  lhs.elem_float3 *= rhs.elem_float3;
+  lhs.elem_float4 *= rhs.elem_float4;
+  lhs.elem_int *= rhs.elem_int;
+  lhs.elem_int2 *= rhs.elem_int2;
+  lhs.elem_int3 *= rhs.elem_int3;
+  lhs.elem_int4 *= rhs.elem_int4;
+  lhs.elem_long *= rhs.elem_long;
+  lhs.elem_long2 *= rhs.elem_long2;
+  lhs.elem_long3 *= rhs.elem_long3;
+  lhs.elem_long4 *= rhs.elem_long4;
+  lhs.elem_short *= rhs.elem_short;
+  lhs.elem_short2 *= rhs.elem_short2;
+  lhs.elem_short3 *= rhs.elem_short3;
+  lhs.elem_short4 *= rhs.elem_short4;
+  lhs.elem_uchar *= rhs.elem_uchar;
+  lhs.elem_uchar2 *= rhs.elem_uchar2;
+  lhs.elem_uchar3 *= rhs.elem_uchar3;
+  lhs.elem_uchar4 *= rhs.elem_uchar4;
+  lhs.elem_uint *= rhs.elem_uint;
+  lhs.elem_uint2 *= rhs.elem_uint2;
+  lhs.elem_uint3 *= rhs.elem_uint3;
+  lhs.elem_uint4 *= rhs.elem_uint4;
+  lhs.elem_ulong *= rhs.elem_ulong;
+  lhs.elem_ulong2 *= rhs.elem_ulong2;
+  lhs.elem_ulong3 *= rhs.elem_ulong3;
+  lhs.elem_ulong4 *= rhs.elem_ulong4;
+  lhs.elem_ushort *= rhs.elem_ushort;
+  lhs.elem_ushort2 *= rhs.elem_ushort2;
+  lhs.elem_ushort3 *= rhs.elem_ushort3;
+  lhs.elem_ushort4 *= rhs.elem_ushort4;
+  return lhs;
+}
diff --git a/slang_backend.cpp b/slang_backend.cpp
index 79cd783..4899790 100644
--- a/slang_backend.cpp
+++ b/slang_backend.cpp
@@ -66,6 +66,7 @@
 #include "slang_rs_context.h"
 #include "slang_rs_export_foreach.h"
 #include "slang_rs_export_func.h"
+#include "slang_rs_export_reduce.h"
 #include "slang_rs_export_type.h"
 #include "slang_rs_export_var.h"
 #include "slang_rs_metadata.h"
@@ -216,8 +217,9 @@
       mSourceMgr(SourceMgr), mAllowRSPrefix(AllowRSPrefix),
       mIsFilterscript(IsFilterscript), mExportVarMetadata(nullptr),
       mExportFuncMetadata(nullptr), mExportForEachNameMetadata(nullptr),
-      mExportForEachSignatureMetadata(nullptr), mExportTypeMetadata(nullptr),
-      mRSObjectSlotsMetadata(nullptr), mRefCount(mContext->getASTContext()),
+      mExportForEachSignatureMetadata(nullptr), mExportReduceMetadata(nullptr),
+      mExportTypeMetadata(nullptr), mRSObjectSlotsMetadata(nullptr),
+      mRefCount(mContext->getASTContext()),
       mASTChecker(Context, Context->getTargetAPI(), IsFilterscript),
       mLLVMContext(llvm::getGlobalContext()), mDiagEngine(*DiagEngine),
       mCodeGenOpts(CodeGenOpts), mPragmas(Pragmas) {
@@ -719,6 +721,26 @@
   }
 }
 
+void Backend::dumpExportReduceInfo(llvm::Module *M) {
+  if (!mExportReduceMetadata) {
+    mExportReduceMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_REDUCE_MN);
+  }
+
+  llvm::SmallVector<llvm::Metadata *, 1> ExportReduceInfo;
+
+  // Add the names of the reduce-style kernel functions to the metadata node.
+  for (auto I = mContext->export_reduce_begin(),
+            E = mContext->export_reduce_end(); I != E; ++I) {
+    ExportReduceInfo.clear();
+
+    ExportReduceInfo.push_back(
+      llvm::MDString::get(mLLVMContext, (*I)->getName().c_str()));
+
+    mExportReduceMetadata->addOperand(
+      llvm::MDNode::get(mLLVMContext, ExportReduceInfo));
+  }
+}
+
 void Backend::dumpExportTypeInfo(llvm::Module *M) {
   llvm::SmallVector<llvm::Metadata *, 1> ExportTypeInfo;
 
@@ -797,6 +819,9 @@
   if (mContext->hasExportForEach())
     dumpExportForEachInfo(M);
 
+  if (mContext->hasExportReduce())
+    dumpExportReduceInfo(M);
+
   if (mContext->hasExportType())
     dumpExportTypeInfo(M);
 }
diff --git a/slang_backend.h b/slang_backend.h
index c3eb3bc..f655ad8 100644
--- a/slang_backend.h
+++ b/slang_backend.h
@@ -93,6 +93,7 @@
   llvm::NamedMDNode *mExportFuncMetadata;
   llvm::NamedMDNode *mExportForEachNameMetadata;
   llvm::NamedMDNode *mExportForEachSignatureMetadata;
+  llvm::NamedMDNode *mExportReduceMetadata;
   llvm::NamedMDNode *mExportTypeMetadata;
   llvm::NamedMDNode *mRSObjectSlotsMetadata;
 
@@ -105,6 +106,7 @@
   void dumpExportVarInfo(llvm::Module *M);
   void dumpExportFunctionInfo(llvm::Module *M);
   void dumpExportForEachInfo(llvm::Module *M);
+  void dumpExportReduceInfo(llvm::Module *M);
   void dumpExportTypeInfo(llvm::Module *M);
 
  protected:
diff --git a/slang_rs_check_ast.cpp b/slang_rs_check_ast.cpp
index 9a6427b..3d3e886 100644
--- a/slang_rs_check_ast.cpp
+++ b/slang_rs_check_ast.cpp
@@ -21,6 +21,7 @@
 #include "slang_assert.h"
 #include "slang.h"
 #include "slang_rs_export_foreach.h"
+#include "slang_rs_export_reduce.h"
 #include "slang_rs_export_type.h"
 
 namespace slang {
@@ -150,14 +151,29 @@
     return;
   }
 
-  // Validate that the kernel attribute is not used with static.
-  if (FD->hasAttr<clang::KernelAttr>() &&
-      FD->getStorageClass() == clang::SC_Static) {
-    Context->ReportError(FD->getLocation(),
-                         "Invalid use of attribute kernel with "
-                         "static function declaration: %0")
+  if (FD->hasAttr<clang::KernelAttr>()) {
+    // Validate that the kernel attribute is not used with static.
+    if (FD->getStorageClass() == clang::SC_Static) {
+      Context->ReportError(FD->getLocation(),
+                           "Invalid use of attribute kernel with "
+                           "static function declaration: %0")
         << FD->getName();
-    mValid = false;
+      mValid = false;
+    }
+
+    // We allow no arguments to the attribute, or an expected single
+    // argument. If there is an expected single argument, we verify
+    // that it is one of the recognized kernel kinds.
+    llvm::StringRef KernelKind =
+      FD->getAttr<clang::KernelAttr>()->getKernelKind();
+
+    if (!KernelKind.empty() && !KernelKind.equals("reduce")) {
+      Context->ReportError(FD->getLocation(),
+                           "Unknown kernel attribute argument '%0' "
+                           "in declaration of function '%1'")
+        << KernelKind << FD->getName();
+      mValid = false;
+    }
   }
 
   clang::QualType resultType = FD->getReturnType().getCanonicalType();
@@ -181,7 +197,8 @@
   }
 
   bool saveKernel = mInKernel;
-  mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, FD);
+  mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, FD) ||
+              RSExportReduce::isRSReduceFunc(mTargetAPI, FD);
 
   if (clang::Stmt *Body = FD->getBody()) {
     Visit(Body);
diff --git a/slang_rs_context.cpp b/slang_rs_context.cpp
index 2c4c6c6..94eb6be 100644
--- a/slang_rs_context.cpp
+++ b/slang_rs_context.cpp
@@ -34,6 +34,7 @@
 #include "slang_assert.h"
 #include "slang_rs_export_foreach.h"
 #include "slang_rs_export_func.h"
+#include "slang_rs_export_reduce.h"
 #include "slang_rs_export_type.h"
 #include "slang_rs_export_var.h"
 #include "slang_rs_exportable.h"
@@ -99,25 +100,37 @@
     return false;
   }
 
+  // Specialized function
   if (RSSpecialFunc::isSpecialRSFunc(mTargetAPI, FD)) {
     // Do not reflect specialized functions like init, dtor, or graphics root.
     return RSSpecialFunc::validateSpecialFuncDecl(mTargetAPI, this, FD);
-  } else if (RSExportForEach::isRSForEachFunc(mTargetAPI, FD)) {
-    RSExportForEach *EFE = RSExportForEach::Create(this, FD);
-    if (EFE == nullptr)
-      return false;
-    else
+  }
+
+  // Foreach kernel
+  if (RSExportForEach::isRSForEachFunc(mTargetAPI, FD)) {
+    if (auto *EFE = RSExportForEach::Create(this, FD)) {
       mExportForEach.push_back(EFE);
+      return true;
+    }
+    return false;
+  }
+
+  // Reduce kernel
+  if (RSExportReduce::isRSReduceFunc(mTargetAPI, FD)) {
+    if (auto *ER = RSExportReduce::Create(this, FD)) {
+      mExportReduce.push_back(ER);
+      return true;
+    }
+    return false;
+  }
+
+  // Invokable
+  if (auto *EF = RSExportFunc::Create(this, FD)) {
+    mExportFuncs.push_back(EF);
     return true;
   }
 
-  RSExportFunc *EF = RSExportFunc::Create(this, FD);
-  if (EF == nullptr)
-    return false;
-  else
-    mExportFuncs.push_back(EF);
-
-  return true;
+  return false;
 }
 
 
diff --git a/slang_rs_context.h b/slang_rs_context.h
index 8e9b577..8cced4d 100644
--- a/slang_rs_context.h
+++ b/slang_rs_context.h
@@ -48,6 +48,7 @@
   class RSExportVar;
   class RSExportFunc;
   class RSExportForEach;
+  class RSExportReduce;
   class RSExportType;
 
 class RSContext {
@@ -60,6 +61,7 @@
   typedef std::list<RSExportVar*> ExportVarList;
   typedef std::list<RSExportFunc*> ExportFuncList;
   typedef std::list<RSExportForEach*> ExportForEachList;
+  typedef std::list<RSExportReduce*> ExportReduceList;
   typedef llvm::StringMap<RSExportType*> ExportTypeMap;
 
  private:
@@ -100,6 +102,7 @@
   ExportVarList mExportVars;
   ExportFuncList mExportFuncs;
   ExportForEachList mExportForEach;
+  ExportReduceList mExportReduce;
   ExportTypeMap mExportTypes;
 
  public:
@@ -198,6 +201,15 @@
   }
   inline bool hasExportForEach() const { return !mExportForEach.empty(); }
 
+  typedef ExportReduceList::const_iterator const_export_reduce_iterator;
+  const_export_reduce_iterator export_reduce_begin() const {
+    return mExportReduce.begin();
+  }
+  const_export_reduce_iterator export_reduce_end() const {
+    return mExportReduce.end();
+  }
+  inline bool hasExportReduce() const { return !mExportReduce.empty(); }
+
   typedef ExportTypeMap::iterator export_type_iterator;
   typedef ExportTypeMap::const_iterator const_export_type_iterator;
   export_type_iterator export_types_begin() { return mExportTypes.begin(); }
diff --git a/slang_rs_export_foreach.cpp b/slang_rs_export_foreach.cpp
index 4886a66..6a0ff90 100644
--- a/slang_rs_export_foreach.cpp
+++ b/slang_rs_export_foreach.cpp
@@ -574,9 +574,9 @@
                                       const clang::FunctionDecl *FD) {
   slangAssert(FD);
 
-  // Anything tagged as a kernel is definitely used with ForEach.
-  if (FD->hasAttr<clang::KernelAttr>()) {
-    return true;
+  // Anything tagged as a kernel("") is definitely used with ForEach.
+  if (auto *Kernel = FD->getAttr<clang::KernelAttr>()) {
+    return Kernel->getKernelKind().empty();
   }
 
   if (RSSpecialFunc::isGraphicsRootRSFunc(targetAPI, FD)) {
diff --git a/slang_rs_export_reduce.cpp b/slang_rs_export_reduce.cpp
new file mode 100644
index 0000000..778be31
--- /dev/null
+++ b/slang_rs_export_reduce.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "slang_rs_export_reduce.h"
+
+#include <algorithm>
+#include <string>
+
+#include "clang/AST/Attr.h"
+
+#include "slang_assert.h"
+#include "slang_rs_context.h"
+#include "slang_rs_export_type.h"
+#include "slang_version.h"
+
+
+namespace {
+
+bool haveReduceInTargetAPI(unsigned int TargetAPI) {
+  return TargetAPI == RS_DEVELOPMENT_API;
+}
+
+} // end anonymous namespace
+
+
+namespace slang {
+
+// Validate the parameters to a reduce kernel, and set up the
+// exportable object if the kernel is valid.
+//
+// This checks that the passed function declaration of a reduce kernel is
+// a function which satisfies all the requirements for a reduce
+// kernel. Namely, we check for:
+//  - correct target API
+//  - correct parameter count
+//  - non void return type
+//  - return type and parameter types match
+//  - no pointer types in signature.
+//
+// We try to report useful errors to the user.
+//
+// On success, this function returns true and sets the fields mIns and
+// mType to point to the arguments and to the kernel type.
+//
+// If an error was detected, this function returns false.
+bool RSExportReduce::validateAndConstructParams(
+    RSContext *Context, const clang::FunctionDecl *FD) {
+  slangAssert(Context && FD);
+  bool Valid = true;
+
+  // Validate API version.
+  if (!haveReduceInTargetAPI(Context->getTargetAPI())) {
+    Context->ReportError(FD->getLocation(),
+                         "Reduce-style kernel %0() unsupported in SDK level %1")
+      << FD->getName() << Context->getTargetAPI();
+    Valid = false;
+  }
+
+  // Validate parameter count.
+  if (FD->getNumParams() != 2) {
+    Context->ReportError(FD->getLocation(),
+                         "Reduce-style kernel %0() must take 2 parameters "
+                         "(found %1).")
+      << FD->getName() << FD->getNumParams();
+    Valid = false;
+  }
+
+  // Validate return type.
+  const clang::QualType ReturnTy = FD->getReturnType().getCanonicalType();
+
+  if (ReturnTy->isVoidType()) {
+    Context->ReportError(FD->getLocation(),
+                         "Reduce-style kernel %0() cannot return void")
+      << FD->getName();
+    Valid = false;
+  } else if (ReturnTy->isPointerType()) {
+    Context->ReportError(FD->getLocation(),
+                         "Reduce-style kernel %0() cannot return a pointer "
+                         "type: %1")
+      << FD->getName() << ReturnTy.getAsString();
+    Valid = false;
+  }
+
+  // Validate parameter types.
+  if (FD->getNumParams() == 0) {
+    return false;
+  }
+
+  const clang::ParmVarDecl &FirstParam = *FD->getParamDecl(0);
+  const clang::QualType FirstParamTy = FirstParam.getType().getCanonicalType();
+
+  for (auto PVD = FD->param_begin(), PE = FD->param_end(); PVD != PE; ++PVD) {
+    const clang::ParmVarDecl &Param = **PVD;
+    const clang::QualType ParamTy = Param.getType().getCanonicalType();
+
+    // Check that the parameter is not a pointer.
+    if (ParamTy->isPointerType()) {
+      Context->ReportError(Param.getLocation(),
+                           "Reduce-style kernel %0() cannot have "
+                           "parameter '%1' of pointer type: '%2'")
+        << FD->getName() << Param.getName() << ParamTy.getAsString();
+      Valid = false;
+    }
+
+    // Check for type mismatch between this parameter and the return type.
+    if (ParamTy != ReturnTy) {
+      Context->ReportError(FD->getLocation(),
+                           "Reduce-style kernel %0() return type '%1' is not "
+                           "the same type as parameter '%2' (type '%3')")
+        << FD->getName() << ReturnTy.getAsString() << Param.getName()
+        << ParamTy.getAsString();
+      Valid = false;
+    }
+
+    // Check for type mismatch between parameters. It is sufficient to check
+    // for a mismatch with the type of the first argument.
+    if (ParamTy != FirstParamTy) {
+      Context->ReportError(FirstParam.getLocation(),
+                           "In reduce-style kernel %0(): parameter '%1' "
+                           "(type '%2') does not have the same type as "
+                           "parameter '%3' (type '%4')")
+        << FD->getName() << FirstParam.getName() << FirstParamTy.getAsString()
+        << Param.getName() << ParamTy.getAsString();
+      Valid = false;
+    }
+  }
+
+  if (Valid) {
+    // If the validation was successful, then populate the fields of
+    // the exportable.
+    if (!(mType = RSExportType::Create(Context, ReturnTy.getTypePtr()))) {
+      // There was an error exporting the type for the reduce kernel.
+      return false;
+    }
+
+    slangAssert(mIns.size() == 2 && FD->param_end() - FD->param_begin() == 2);
+    std::copy(FD->param_begin(), FD->param_end(), mIns.begin());
+  }
+
+  return Valid;
+}
+
+RSExportReduce *RSExportReduce::Create(RSContext *Context,
+                                       const clang::FunctionDecl *FD) {
+  slangAssert(Context && FD);
+  llvm::StringRef Name = FD->getName();
+
+  slangAssert(!Name.empty() && "Function must have a name");
+
+  RSExportReduce *RE = new RSExportReduce(Context, Name);
+
+  if (!RE->validateAndConstructParams(Context, FD)) {
+    // Don't delete RE here - owned by Context.
+    return nullptr;
+  }
+
+  return RE;
+}
+
+bool RSExportReduce::isRSReduceFunc(unsigned int /* targetAPI */,
+                                    const clang::FunctionDecl *FD) {
+  slangAssert(FD);
+  clang::KernelAttr *KernelAttrOrNull = FD->getAttr<clang::KernelAttr>();
+  return KernelAttrOrNull && KernelAttrOrNull->getKernelKind().equals("reduce");
+}
+
+}  // namespace slang
diff --git a/slang_rs_export_reduce.h b/slang_rs_export_reduce.h
new file mode 100644
index 0000000..9df27ae
--- /dev/null
+++ b/slang_rs_export_reduce.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_EXPORT_REDUCE_H_  // NOLINT
+#define _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_EXPORT_REDUCE_H_
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "slang_rs_context.h"
+#include "slang_rs_exportable.h"
+#include "slang_rs_export_type.h"
+
+namespace clang {
+  class FunctionDecl;
+}  // namespace clang
+
+namespace slang {
+
+// Base class for reflecting control-side reduce
+class RSExportReduce : public RSExportable {
+ public:
+  typedef llvm::SmallVectorImpl<const clang::ParmVarDecl*> InVec;
+  typedef InVec::const_iterator InIter;
+
+ private:
+  // Function name
+  std::string mName;
+  // Input and output type
+  RSExportType *mType;
+  // Inputs
+  llvm::SmallVector<const clang::ParmVarDecl *, 2> mIns;
+
+  RSExportReduce(RSContext *Context, const llvm::StringRef &Name)
+    : RSExportable(Context, RSExportable::EX_REDUCE),
+      mName(Name.data(), Name.size()), mType(nullptr), mIns(2) {
+  }
+
+  RSExportReduce(const RSExportReduce &) = delete;
+  RSExportReduce &operator=(const RSExportReduce &) = delete;
+
+  // Given a reduce kernel declaration, validate the parameters to the
+  // reduce kernel.
+  bool validateAndConstructParams(RSContext *Context,
+                                  const clang::FunctionDecl *FD);
+
+ public:
+  static RSExportReduce *Create(RSContext *Context,
+                                const clang::FunctionDecl *FD);
+
+  const std::string &getName() const {
+    return mName;
+  }
+
+  const InVec &getIns() const {
+    return mIns;
+  }
+
+  const RSExportType *getType() const {
+    return mType;
+  }
+
+  static bool isRSReduceFunc(unsigned int targetAPI,
+                             const clang::FunctionDecl *FD);
+
+};  // RSExportReduce
+
+}  // namespace slang
+
+#endif  // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_EXPORT_REDUCE_H_  NOLINT
diff --git a/slang_rs_exportable.h b/slang_rs_exportable.h
index 0871be3..e8fc11e 100644
--- a/slang_rs_exportable.h
+++ b/slang_rs_exportable.h
@@ -27,7 +27,8 @@
     EX_FUNC,
     EX_TYPE,
     EX_VAR,
-    EX_FOREACH
+    EX_FOREACH,
+    EX_REDUCE
   };
 
  private:
diff --git a/tests/F_kernel_badattr/kernel_badattr.rs b/tests/F_kernel_badattr/kernel_badattr.rs
new file mode 100644
index 0000000..a719bd9
--- /dev/null
+++ b/tests/F_kernel_badattr/kernel_badattr.rs
@@ -0,0 +1,14 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel("unimplemented"))) kernel(int arg) {
+  return 0;
+}
+
+int __attribute__((kernel(7))) kernel2(int arg) {
+  return 0;
+}
+
+int __attribute__((kernel("reduce", 1))) kernel3(int arg) {
+  return 0;
+}
diff --git a/tests/F_kernel_badattr/stderr.txt.expect b/tests/F_kernel_badattr/stderr.txt.expect
new file mode 100644
index 0000000..6cd1c74
--- /dev/null
+++ b/tests/F_kernel_badattr/stderr.txt.expect
@@ -0,0 +1,3 @@
+kernel_badattr.rs:8:27: error: 'kernel' attribute requires a string
+kernel_badattr.rs:12:20: error: 'kernel' attribute takes no more than 1 argument
+kernel_badattr.rs:4:46: error: Unknown kernel attribute argument 'unimplemented' in declaration of function 'kernel'
diff --git a/tests/F_kernel_badattr/stdout.txt.expect b/tests/F_kernel_badattr/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_kernel_badattr/stdout.txt.expect
diff --git a/tests/F_reduce_api_unsupported/reduce_api_unsupported.rs b/tests/F_reduce_api_unsupported/reduce_api_unsupported.rs
new file mode 100644
index 0000000..8dfedfb
--- /dev/null
+++ b/tests/F_reduce_api_unsupported/reduce_api_unsupported.rs
@@ -0,0 +1,12 @@
+// -target-api 23
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+typedef struct foo {
+   int x;
+} foo;
+
+foo __attribute__((kernel("reduce"))) addFoo(foo a, foo b) {
+  foo result = { a.x + b.x };
+  return result;
+}
diff --git a/tests/F_reduce_api_unsupported/stderr.txt.expect b/tests/F_reduce_api_unsupported/stderr.txt.expect
new file mode 100644
index 0000000..0e389a4
--- /dev/null
+++ b/tests/F_reduce_api_unsupported/stderr.txt.expect
@@ -0,0 +1 @@
+reduce_api_unsupported.rs:9:39: error: Reduce-style kernel addFoo() unsupported in SDK level 23
diff --git a/tests/F_reduce_api_unsupported/stdout.txt.expect b/tests/F_reduce_api_unsupported/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_api_unsupported/stdout.txt.expect
diff --git a/tests/F_reduce_non_binary/reduce_non_binary.rs b/tests/F_reduce_non_binary/reduce_non_binary.rs
new file mode 100644
index 0000000..77fdaa1
--- /dev/null
+++ b/tests/F_reduce_non_binary/reduce_non_binary.rs
@@ -0,0 +1,27 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+/* 0 arguments */
+
+int __attribute__((kernel("reduce"))) kernel0(void) {
+  return 0;
+}
+
+/* 1 argument */
+
+int __attribute__((kernel("reduce"))) kernel1(int arg1) {
+  return 0;
+}
+
+/* 3 arguments */
+
+int __attribute__((kernel("reduce"))) kernel3(int arg1, int arg2, int arg3) {
+  return 0;
+}
+
+/* 4 arguments */
+
+int __attribute__((kernel("reduce"))) kernel4(int arg1, int arg2, int arg3, int arg4) {
+  return 0;
+}
diff --git a/tests/F_reduce_non_binary/stderr.txt.expect b/tests/F_reduce_non_binary/stderr.txt.expect
new file mode 100644
index 0000000..1463a21
--- /dev/null
+++ b/tests/F_reduce_non_binary/stderr.txt.expect
@@ -0,0 +1,4 @@
+reduce_non_binary.rs:7:39: error: Reduce-style kernel kernel0() must take 2 parameters (found 0).
+reduce_non_binary.rs:13:39: error: Reduce-style kernel kernel1() must take 2 parameters (found 1).
+reduce_non_binary.rs:19:39: error: Reduce-style kernel kernel3() must take 2 parameters (found 3).
+reduce_non_binary.rs:25:39: error: Reduce-style kernel kernel4() must take 2 parameters (found 4).
diff --git a/tests/F_reduce_non_binary/stdout.txt.expect b/tests/F_reduce_non_binary/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_non_binary/stdout.txt.expect
diff --git a/tests/F_reduce_param_type_mismatch/reduce_param_type_mismatch.rs b/tests/F_reduce_param_type_mismatch/reduce_param_type_mismatch.rs
new file mode 100644
index 0000000..fd8e1aa
--- /dev/null
+++ b/tests/F_reduce_param_type_mismatch/reduce_param_type_mismatch.rs
@@ -0,0 +1,7 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel("reduce"))) kernel1(int arg1, float arg2) {
+  return 0;
+}
diff --git a/tests/F_reduce_param_type_mismatch/stderr.txt.expect b/tests/F_reduce_param_type_mismatch/stderr.txt.expect
new file mode 100644
index 0000000..cc55737
--- /dev/null
+++ b/tests/F_reduce_param_type_mismatch/stderr.txt.expect
@@ -0,0 +1,2 @@
+reduce_param_type_mismatch.rs:5:39: error: Reduce-style kernel kernel1() return type 'int' is not the same type as parameter 'arg2' (type 'float')
+reduce_param_type_mismatch.rs:5:51: error: In reduce-style kernel kernel1(): parameter 'arg1' (type 'int') does not have the same type as parameter 'arg2' (type 'float')
diff --git a/tests/F_reduce_param_type_mismatch/stdout.txt.expect b/tests/F_reduce_param_type_mismatch/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_param_type_mismatch/stdout.txt.expect
diff --git a/tests/F_reduce_ptr_param/reduce_ptr_param.rs b/tests/F_reduce_ptr_param/reduce_ptr_param.rs
new file mode 100644
index 0000000..f099e19
--- /dev/null
+++ b/tests/F_reduce_ptr_param/reduce_ptr_param.rs
@@ -0,0 +1,7 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel("reduce"))) kernel(int *arg1, int *arg2) {
+  return 0;
+}
diff --git a/tests/F_reduce_ptr_param/stderr.txt.expect b/tests/F_reduce_ptr_param/stderr.txt.expect
new file mode 100644
index 0000000..3dc971e
--- /dev/null
+++ b/tests/F_reduce_ptr_param/stderr.txt.expect
@@ -0,0 +1,4 @@
+reduce_ptr_param.rs:5:51: error: Reduce-style kernel kernel() cannot have parameter 'arg1' of pointer type: 'int *'
+reduce_ptr_param.rs:5:39: error: Reduce-style kernel kernel() return type 'int' is not the same type as parameter 'arg1' (type 'int *')
+reduce_ptr_param.rs:5:62: error: Reduce-style kernel kernel() cannot have parameter 'arg2' of pointer type: 'int *'
+reduce_ptr_param.rs:5:39: error: Reduce-style kernel kernel() return type 'int' is not the same type as parameter 'arg2' (type 'int *')
diff --git a/tests/F_reduce_ptr_param/stdout.txt.expect b/tests/F_reduce_ptr_param/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_ptr_param/stdout.txt.expect
diff --git a/tests/F_reduce_ptr_ret_val/reduce_ptr_ret_val.rs b/tests/F_reduce_ptr_ret_val/reduce_ptr_ret_val.rs
new file mode 100644
index 0000000..b1ee9c9
--- /dev/null
+++ b/tests/F_reduce_ptr_ret_val/reduce_ptr_ret_val.rs
@@ -0,0 +1,7 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int *__attribute__((kernel("reduce"))) kernel(int arg1, int arg2) {
+  return 0;
+}
diff --git a/tests/F_reduce_ptr_ret_val/stderr.txt.expect b/tests/F_reduce_ptr_ret_val/stderr.txt.expect
new file mode 100644
index 0000000..5e5852e
--- /dev/null
+++ b/tests/F_reduce_ptr_ret_val/stderr.txt.expect
@@ -0,0 +1,3 @@
+reduce_ptr_ret_val.rs:5:40: error: Reduce-style kernel kernel() cannot return a pointer type: int *
+reduce_ptr_ret_val.rs:5:40: error: Reduce-style kernel kernel() return type 'int *' is not the same type as parameter 'arg1' (type 'int')
+reduce_ptr_ret_val.rs:5:40: error: Reduce-style kernel kernel() return type 'int *' is not the same type as parameter 'arg2' (type 'int')
diff --git a/tests/F_reduce_ptr_ret_val/stdout.txt.expect b/tests/F_reduce_ptr_ret_val/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_ptr_ret_val/stdout.txt.expect
diff --git a/tests/F_reduce_ret_type_mismatch/reduce_ret_type_mismatch.rs b/tests/F_reduce_ret_type_mismatch/reduce_ret_type_mismatch.rs
new file mode 100644
index 0000000..e43ca89
--- /dev/null
+++ b/tests/F_reduce_ret_type_mismatch/reduce_ret_type_mismatch.rs
@@ -0,0 +1,7 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+double __attribute__((kernel("reduce"))) kernel(float arg1, float arg2) {
+  return arg1 + arg2;
+}
diff --git a/tests/F_reduce_ret_type_mismatch/stderr.txt.expect b/tests/F_reduce_ret_type_mismatch/stderr.txt.expect
new file mode 100644
index 0000000..e616c94
--- /dev/null
+++ b/tests/F_reduce_ret_type_mismatch/stderr.txt.expect
@@ -0,0 +1,2 @@
+reduce_ret_type_mismatch.rs:5:42: error: Reduce-style kernel kernel() return type 'double' is not the same type as parameter 'arg1' (type 'float')
+reduce_ret_type_mismatch.rs:5:42: error: Reduce-style kernel kernel() return type 'double' is not the same type as parameter 'arg2' (type 'float')
diff --git a/tests/F_reduce_ret_type_mismatch/stdout.txt.expect b/tests/F_reduce_ret_type_mismatch/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_ret_type_mismatch/stdout.txt.expect
diff --git a/tests/F_reduce_void_ret/reduce_void_ret.rs b/tests/F_reduce_void_ret/reduce_void_ret.rs
new file mode 100644
index 0000000..8fecfc2
--- /dev/null
+++ b/tests/F_reduce_void_ret/reduce_void_ret.rs
@@ -0,0 +1,7 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void __attribute__((kernel("reduce"))) kernel(int arg1, int arg2) {
+  return;
+}
diff --git a/tests/F_reduce_void_ret/stderr.txt.expect b/tests/F_reduce_void_ret/stderr.txt.expect
new file mode 100644
index 0000000..ecb3dd1
--- /dev/null
+++ b/tests/F_reduce_void_ret/stderr.txt.expect
@@ -0,0 +1,3 @@
+reduce_void_ret.rs:5:40: error: Reduce-style kernel kernel() cannot return void
+reduce_void_ret.rs:5:40: error: Reduce-style kernel kernel() return type 'void' is not the same type as parameter 'arg1' (type 'int')
+reduce_void_ret.rs:5:40: error: Reduce-style kernel kernel() return type 'void' is not the same type as parameter 'arg2' (type 'int')
diff --git a/tests/F_reduce_void_ret/stdout.txt.expect b/tests/F_reduce_void_ret/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_reduce_void_ret/stdout.txt.expect
diff --git a/tests/P_reduce/reduce.rs b/tests/P_reduce/reduce.rs
new file mode 100644
index 0000000..4017f79
--- /dev/null
+++ b/tests/P_reduce/reduce.rs
@@ -0,0 +1,298 @@
+// -target-api 0
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+bool __attribute__((kernel("reduce")))
+mul_bool(bool lhs, bool rhs) {
+  return lhs * rhs;
+}
+
+char __attribute__((kernel("reduce")))
+mul_char(char lhs, char rhs) {
+  return lhs * rhs;
+}
+
+char2 __attribute__((kernel("reduce")))
+mul_char2(char2 lhs, char2 rhs) {
+  return lhs * rhs;
+}
+
+char3 __attribute__((kernel("reduce")))
+mul_char3(char3 lhs, char3 rhs) {
+  return lhs * rhs;
+}
+
+char4 __attribute__((kernel("reduce")))
+mul_char4(char4 lhs, char4 rhs) {
+  return lhs * rhs;
+}
+
+double __attribute__((kernel("reduce")))
+mul_double(double lhs, double rhs) {
+  return lhs * rhs;
+}
+
+double2 __attribute__((kernel("reduce")))
+mul_double2(double2 lhs, double2 rhs) {
+  return lhs * rhs;
+}
+
+double3 __attribute__((kernel("reduce")))
+mul_double3(double3 lhs, double3 rhs) {
+  return lhs * rhs;
+}
+
+double4 __attribute__((kernel("reduce")))
+mul_double4(double4 lhs, double4 rhs) {
+  return lhs * rhs;
+}
+
+float __attribute__((kernel("reduce")))
+mul_float(float lhs, float rhs) {
+  return lhs * rhs;
+}
+
+float2 __attribute__((kernel("reduce")))
+mul_float2(float2 lhs, float2 rhs) {
+  return lhs * rhs;
+}
+
+float3 __attribute__((kernel("reduce")))
+mul_float3(float3 lhs, float3 rhs) {
+  return lhs * rhs;
+}
+
+float4 __attribute__((kernel("reduce")))
+mul_float4(float4 lhs, float4 rhs) {
+  return lhs * rhs;
+}
+
+int __attribute__((kernel("reduce")))
+mul_int(int lhs, int rhs) {
+  return lhs * rhs;
+}
+
+int2 __attribute__((kernel("reduce")))
+mul_int2(int2 lhs, int2 rhs) {
+  return lhs * rhs;
+}
+
+int3 __attribute__((kernel("reduce")))
+mul_int3(int3 lhs, int3 rhs) {
+  return lhs * rhs;
+}
+
+int4 __attribute__((kernel("reduce")))
+mul_int4(int4 lhs, int4 rhs) {
+  return lhs * rhs;
+}
+
+long __attribute__((kernel("reduce")))
+mul_long(long lhs, long rhs) {
+  return lhs * rhs;
+}
+
+long2 __attribute__((kernel("reduce")))
+mul_long2(long2 lhs, long2 rhs) {
+  return lhs * rhs;
+}
+
+long3 __attribute__((kernel("reduce")))
+mul_long3(long3 lhs, long3 rhs) {
+  return lhs * rhs;
+}
+
+long4 __attribute__((kernel("reduce")))
+mul_long4(long4 lhs, long4 rhs) {
+  return lhs * rhs;
+}
+
+short __attribute__((kernel("reduce")))
+mul_short(short lhs, short rhs) {
+  return lhs * rhs;
+}
+
+short2 __attribute__((kernel("reduce")))
+mul_short2(short2 lhs, short2 rhs) {
+  return lhs * rhs;
+}
+
+short3 __attribute__((kernel("reduce")))
+mul_short3(short3 lhs, short3 rhs) {
+  return lhs * rhs;
+}
+
+short4 __attribute__((kernel("reduce")))
+mul_short4(short4 lhs, short4 rhs) {
+  return lhs * rhs;
+}
+
+uchar __attribute__((kernel("reduce")))
+mul_uchar(uchar lhs, uchar rhs) {
+  return lhs * rhs;
+}
+
+uchar2 __attribute__((kernel("reduce")))
+mul_uchar2(uchar2 lhs, uchar2 rhs) {
+  return lhs * rhs;
+}
+
+uchar3 __attribute__((kernel("reduce")))
+mul_uchar3(uchar3 lhs, uchar3 rhs) {
+  return lhs * rhs;
+}
+
+uchar4 __attribute__((kernel("reduce")))
+mul_uchar4(uchar4 lhs, uchar4 rhs) {
+  return lhs * rhs;
+}
+
+uint __attribute__((kernel("reduce")))
+mul_uint(uint lhs, uint rhs) {
+  return lhs * rhs;
+}
+
+uint2 __attribute__((kernel("reduce")))
+mul_uint2(uint2 lhs, uint2 rhs) {
+  return lhs * rhs;
+}
+
+uint3 __attribute__((kernel("reduce")))
+mul_uint3(uint3 lhs, uint3 rhs) {
+  return lhs * rhs;
+}
+
+uint4 __attribute__((kernel("reduce")))
+mul_uint4(uint4 lhs, uint4 rhs) {
+  return lhs * rhs;
+}
+
+ulong __attribute__((kernel("reduce")))
+mul_ulong(ulong lhs, ulong rhs) {
+  return lhs * rhs;
+}
+
+ulong2 __attribute__((kernel("reduce")))
+mul_ulong2(ulong2 lhs, ulong2 rhs) {
+  return lhs * rhs;
+}
+
+ulong3 __attribute__((kernel("reduce")))
+mul_ulong3(ulong3 lhs, ulong3 rhs) {
+  return lhs * rhs;
+}
+
+ulong4 __attribute__((kernel("reduce")))
+mul_ulong4(ulong4 lhs, ulong4 rhs) {
+  return lhs * rhs;
+}
+
+ushort __attribute__((kernel("reduce")))
+mul_ushort(ushort lhs, ushort rhs) {
+  return lhs * rhs;
+}
+
+ushort2 __attribute__((kernel("reduce")))
+mul_ushort2(ushort2 lhs, ushort2 rhs) {
+  return lhs * rhs;
+}
+
+ushort3 __attribute__((kernel("reduce")))
+mul_ushort3(ushort3 lhs, ushort3 rhs) {
+  return lhs * rhs;
+}
+
+ushort4 __attribute__((kernel("reduce")))
+mul_ushort4(ushort4 lhs, ushort4 rhs) {
+  return lhs * rhs;
+}
+
+struct indirect {
+  bool elem_bool;
+  char elem_char;
+  char2 elem_char2;
+  char3 elem_char3;
+  char4 elem_char4;
+  double elem_double;
+  double2 elem_double2;
+  double3 elem_double3;
+  double4 elem_double4;
+  float elem_float;
+  float2 elem_float2;
+  float3 elem_float3;
+  float4 elem_float4;
+  int elem_int;
+  int2 elem_int2;
+  int3 elem_int3;
+  int4 elem_int4;
+  long elem_long;
+  long2 elem_long2;
+  long3 elem_long3;
+  long4 elem_long4;
+  short elem_short;
+  short2 elem_short2;
+  short3 elem_short3;
+  short4 elem_short4;
+  uchar elem_uchar;
+  uchar2 elem_uchar2;
+  uchar3 elem_uchar3;
+  uchar4 elem_uchar4;
+  uint elem_uint;
+  uint2 elem_uint2;
+  uint3 elem_uint3;
+  uint4 elem_uint4;
+  ulong elem_ulong;
+  ulong2 elem_ulong2;
+  ulong3 elem_ulong3;
+  ulong4 elem_ulong4;
+  ushort elem_ushort;
+  ushort2 elem_ushort2;
+  ushort3 elem_ushort3;
+  ushort4 elem_ushort4;
+};
+
+struct indirect __attribute__((kernel("reduce")))
+mul_indirect(struct indirect lhs, struct indirect rhs) {
+  lhs.elem_bool *= rhs.elem_bool;
+  lhs.elem_char *= rhs.elem_char;
+  lhs.elem_char2 *= rhs.elem_char2;
+  lhs.elem_char3 *= rhs.elem_char3;
+  lhs.elem_char4 *= rhs.elem_char4;
+  lhs.elem_double *= rhs.elem_double;
+  lhs.elem_double2 *= rhs.elem_double2;
+  lhs.elem_double3 *= rhs.elem_double3;
+  lhs.elem_double4 *= rhs.elem_double4;
+  lhs.elem_float *= rhs.elem_float;
+  lhs.elem_float2 *= rhs.elem_float2;
+  lhs.elem_float3 *= rhs.elem_float3;
+  lhs.elem_float4 *= rhs.elem_float4;
+  lhs.elem_int *= rhs.elem_int;
+  lhs.elem_int2 *= rhs.elem_int2;
+  lhs.elem_int3 *= rhs.elem_int3;
+  lhs.elem_int4 *= rhs.elem_int4;
+  lhs.elem_long *= rhs.elem_long;
+  lhs.elem_long2 *= rhs.elem_long2;
+  lhs.elem_long3 *= rhs.elem_long3;
+  lhs.elem_long4 *= rhs.elem_long4;
+  lhs.elem_short *= rhs.elem_short;
+  lhs.elem_short2 *= rhs.elem_short2;
+  lhs.elem_short3 *= rhs.elem_short3;
+  lhs.elem_short4 *= rhs.elem_short4;
+  lhs.elem_uchar *= rhs.elem_uchar;
+  lhs.elem_uchar2 *= rhs.elem_uchar2;
+  lhs.elem_uchar3 *= rhs.elem_uchar3;
+  lhs.elem_uchar4 *= rhs.elem_uchar4;
+  lhs.elem_uint *= rhs.elem_uint;
+  lhs.elem_uint2 *= rhs.elem_uint2;
+  lhs.elem_uint3 *= rhs.elem_uint3;
+  lhs.elem_uint4 *= rhs.elem_uint4;
+  lhs.elem_ulong *= rhs.elem_ulong;
+  lhs.elem_ulong2 *= rhs.elem_ulong2;
+  lhs.elem_ulong3 *= rhs.elem_ulong3;
+  lhs.elem_ulong4 *= rhs.elem_ulong4;
+  lhs.elem_ushort *= rhs.elem_ushort;
+  lhs.elem_ushort2 *= rhs.elem_ushort2;
+  lhs.elem_ushort3 *= rhs.elem_ushort3;
+  lhs.elem_ushort4 *= rhs.elem_ushort4;
+  return lhs;
+}
diff --git a/tests/P_reduce/stderr.txt.expect b/tests/P_reduce/stderr.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_reduce/stderr.txt.expect
diff --git a/tests/P_reduce/stdout.txt.expect b/tests/P_reduce/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_reduce/stdout.txt.expect