Handle kernel launch calls

Bug: 23535985

Find rsParallelFor(void*, ...) calls in the .rs source code and
translate them into rsForEachInternal(int, rs_allocation, rs_allocation)
calls.

Semantic checks are pretty limited at this point. Will enhance them in followup CLs.

Change-Id: I6e2cf3db868f426aa8e0b9a77732b66c1e6b9f03
diff --git a/Android.mk b/Android.mk
index e2b991d..3f56193 100644
--- a/Android.mk
+++ b/Android.mk
@@ -148,6 +148,7 @@
 LOCAL_SRC_FILES :=	\
 	llvm-rs-cc.cpp	\
 	rs_cc_options.cpp \
+	slang_rs_foreach_lowering.cpp \
 	slang_rs_ast_replace.cpp	\
 	slang_rs_check_ast.cpp	\
 	slang_rs_context.cpp	\
diff --git a/slang_backend.cpp b/slang_backend.cpp
index 83c157f..5ef86de 100644
--- a/slang_backend.cpp
+++ b/slang_backend.cpp
@@ -223,6 +223,7 @@
       mExportTypeMetadata(nullptr), mRSObjectSlotsMetadata(nullptr),
       mRefCount(mContext->getASTContext()),
       mASTChecker(Context, Context->getTargetAPI(), IsFilterscript),
+      mForEachHandler(Context),
       mLLVMContext(llvm::getGlobalContext()), mDiagEngine(*DiagEngine),
       mCodeGenOpts(CodeGenOpts), mPragmas(Pragmas) {
   mGen = CreateLLVMCodeGen(mDiagEngine, "", mCodeGenOpts, mLLVMContext);
@@ -367,7 +368,32 @@
   }
 }
 
+void Backend::LowerRSForEachCall(clang::FunctionDecl *FD) {
+  // Skip this AST walking for lower API levels.
+  if (getTargetAPI() < SLANG_DEVELOPMENT_TARGET_API) {
+    return;
+  }
+
+  if (!FD || !FD->hasBody() ||
+      Slang::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) {
+    return;
+  }
+
+  mForEachHandler.VisitStmt(FD->getBody());
+}
+
 bool Backend::HandleTopLevelDecl(clang::DeclGroupRef D) {
+  // Find and remember the TypeDecl for rs_allocation so we can use it
+  // later during the compilation
+  for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end();
+       I != E; I++) {
+    clang::TypeDecl* TD = llvm::dyn_cast<clang::TypeDecl>(*I);
+    if (TD && TD->getName().equals("rs_allocation")) {
+      mContext->addAllocationType(TD);
+      break;
+    }
+  }
+
   // Disallow user-defined functions with prefix "rs"
   if (!mAllowRSPrefix) {
     // Iterate all function declarations in the program.
@@ -386,6 +412,8 @@
     }
   }
 
+  mContext->processExportDecl(D);
+
   // Process any non-static function declarations
   for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; I++) {
     clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I);
@@ -405,7 +433,10 @@
       }
       AnnotateFunction(FD);
     }
+
+    LowerRSForEachCall(FD);
   }
+
   return mGen->HandleTopLevelDecl(D);
 }
 
@@ -670,10 +701,11 @@
 
           CI->setCallingConv(F->getCallingConv());
 
-          if (F->getReturnType() == llvm::Type::getVoidTy(mLLVMContext))
+          if (F->getReturnType() == llvm::Type::getVoidTy(mLLVMContext)) {
             IB->CreateRetVoid();
-          else
+          } else {
             IB->CreateRet(CI);
+          }
 
           delete IB;
         }
diff --git a/slang_backend.h b/slang_backend.h
index 83912fd..2c3b9b9 100644
--- a/slang_backend.h
+++ b/slang_backend.h
@@ -26,6 +26,7 @@
 #include "slang.h"
 #include "slang_pragma_recorder.h"
 #include "slang_rs_check_ast.h"
+#include "slang_rs_foreach_lowering.h"
 #include "slang_rs_object_ref_count.h"
 #include "slang_version.h"
 
@@ -103,6 +104,8 @@
 
   RSCheckAST mASTChecker;
 
+  RSForEachLowering mForEachHandler;
+
   void AnnotateFunction(clang::FunctionDecl *FD);
 
   void dumpExportVarInfo(llvm::Module *M);
@@ -111,6 +114,8 @@
   void dumpExportReduceInfo(llvm::Module *M);
   void dumpExportTypeInfo(llvm::Module *M);
 
+  void LowerRSForEachCall(clang::FunctionDecl* FD);
+
  protected:
   llvm::LLVMContext &mLLVMContext;
   clang::DiagnosticsEngine &mDiagEngine;
diff --git a/slang_rs_context.cpp b/slang_rs_context.cpp
index 94eb6be..e840876 100644
--- a/slang_rs_context.cpp
+++ b/slang_rs_context.cpp
@@ -19,6 +19,7 @@
 #include <string>
 
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/Mangle.h"
@@ -67,6 +68,9 @@
 
   // Prepare target data
   mDataLayout = new llvm::DataLayout(Target.getTargetDescription());
+
+  // Reserve slot 0 for the root kernel.
+  mExportForEach.push_back(nullptr);
 }
 
 bool RSContext::processExportVar(const clang::VarDecl *VD) {
@@ -87,6 +91,19 @@
   return true;
 }
 
+int RSContext::getForEachSlotNumber(const clang::FunctionDecl* FD) {
+  const clang::StringRef& funcName = FD->getName();
+  return getForEachSlotNumber(funcName);
+}
+
+int RSContext::getForEachSlotNumber(const clang::StringRef& funcName) {
+  auto it = mExportForEachMap.find(funcName);
+  if (it == mExportForEachMap.end()) {
+    return -1;
+  }
+  return it->second;
+}
+
 bool RSContext::processExportFunc(const clang::FunctionDecl *FD) {
   slangAssert(!FD->getName().empty() && "Function name should not be empty");
 
@@ -108,11 +125,7 @@
 
   // Foreach kernel
   if (RSExportForEach::isRSForEachFunc(mTargetAPI, FD)) {
-    if (auto *EFE = RSExportForEach::Create(this, FD)) {
-      mExportForEach.push_back(EFE);
-      return true;
-    }
-    return false;
+    return addForEach(FD);
   }
 
   // Reduce kernel
@@ -133,6 +146,25 @@
   return false;
 }
 
+bool RSContext::addForEach(const clang::FunctionDecl* FD) {
+  RSExportForEach *EFE = RSExportForEach::Create(this, FD);
+  if (EFE == nullptr) {
+    return false;
+  }
+
+  const llvm::StringRef& funcName = FD->getName();
+
+  if (funcName.equals("root")) {
+    // The root kernel should always be in slot 0.
+    mExportForEachMap.insert(std::make_pair(funcName, 0));
+    mExportForEach[0] = EFE;
+  } else {
+    mExportForEachMap.insert(std::make_pair(funcName, mExportForEach.size()));
+    mExportForEach.push_back(EFE);
+  }
+
+  return true;
+}
 
 bool RSContext::processExportType(const llvm::StringRef &Name) {
   clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl();
@@ -178,41 +210,34 @@
   return (ET != nullptr);
 }
 
-
-// Possibly re-order ForEach exports (maybe generating a dummy "root" function).
-// We require "root" to be listed as slot 0 of our exported compute kernels,
-// so this only needs to be created if we have other non-root kernels.
-void RSContext::cleanupForEach() {
-  bool foundNonRoot = false;
-  ExportForEachList::iterator begin = mExportForEach.begin();
-
-  for (ExportForEachList::iterator I = begin, E = mExportForEach.end();
-       I != E;
-       I++) {
-    RSExportForEach *EFE = *I;
-    if (!EFE->getName().compare("root")) {
-      if (I == begin) {
-        // Nothing to do, since it is the first function
-        return;
-      }
-
-      mExportForEach.erase(I);
-      mExportForEach.push_front(EFE);
-      return;
-    } else {
-      foundNonRoot = true;
-    }
-  }
-
-  // If we found a non-root kernel, but no root() function, we need to add a
-  // dummy version (so that script->script calls of rsForEach don't behave
-  // erratically).
-  if (foundNonRoot) {
-    RSExportForEach *DummyRoot = RSExportForEach::CreateDummyRoot(this);
-    mExportForEach.push_front(DummyRoot);
-  }
+void RSContext::addAllocationType(const clang::TypeDecl* TD) {
+  mAllocationType = mCtx.getTypeDeclType(TD);
 }
 
+bool RSContext::processExportDecl(const clang::DeclGroupRef& DGR) {
+  bool valid = true;
+  for (auto I = DGR.begin(), E = DGR.end(); valid && I != E; I++) {
+    clang::Decl* D = *I;
+    switch (D->getKind()) {
+    case clang::Decl::Var: {
+      clang::VarDecl* VD = llvm::dyn_cast<clang::VarDecl>(D);
+      if (VD->getFormalLinkage() == clang::ExternalLinkage) {
+        valid = processExportVar(VD);
+      }
+      break;
+    }
+    case clang::Decl::Function: {
+      clang::FunctionDecl* FD = llvm::dyn_cast<clang::FunctionDecl>(D);
+      if (FD->getFormalLinkage() == clang::ExternalLinkage) {
+        valid = processExportFunc(FD);
+      }
+      break;
+    }
+    default: break;
+    }
+  }
+  return valid;
+}
 
 bool RSContext::processExport() {
   bool valid = true;
@@ -221,32 +246,9 @@
     return false;
   }
 
-  // Export variable
-  clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl();
-  for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
-           DE = TUDecl->decls_end();
-       DI != DE;
-       DI++) {
-    if (DI->getKind() == clang::Decl::Var) {
-      clang::VarDecl *VD = (clang::VarDecl*) (*DI);
-      if (VD->getFormalLinkage() == clang::ExternalLinkage) {
-        if (!processExportVar(VD)) {
-          valid = false;
-        }
-      }
-    } else if (DI->getKind() == clang::Decl::Function) {
-      // Export functions
-      clang::FunctionDecl *FD = (clang::FunctionDecl*) (*DI);
-      if (FD->getFormalLinkage() == clang::ExternalLinkage) {
-        if (!processExportFunc(FD)) {
-          valid = false;
-        }
-      }
-    }
-  }
-
-  if (valid) {
-    cleanupForEach();
+  // Create a dummy root in slot 0 if a root kernel is not seen.
+  if (mExportForEach[0] == nullptr) {
+    mExportForEach[0] = RSExportForEach::CreateDummyRoot(this);
   }
 
   // Finally, export type forcely set to be exported by user
diff --git a/slang_rs_context.h b/slang_rs_context.h
index 8cced4d..78d3cb8 100644
--- a/slang_rs_context.h
+++ b/slang_rs_context.h
@@ -21,6 +21,7 @@
 #include <list>
 #include <map>
 #include <string>
+#include <vector>
 
 #include "clang/Lex/Preprocessor.h"
 #include "clang/AST/Mangle.h"
@@ -39,8 +40,11 @@
   class VarDecl;
   class ASTContext;
   class TargetInfo;
+  class DeclGroupRef;
   class FunctionDecl;
+  class QualType;
   class SourceManager;
+  class TypeDecl;
 }   // namespace clang
 
 namespace slang {
@@ -60,7 +64,7 @@
   typedef std::list<RSExportable*> ExportableList;
   typedef std::list<RSExportVar*> ExportVarList;
   typedef std::list<RSExportFunc*> ExportFuncList;
-  typedef std::list<RSExportForEach*> ExportForEachList;
+  typedef std::vector<RSExportForEach*> ExportForEachVector;
   typedef std::list<RSExportReduce*> ExportReduceList;
   typedef llvm::StringMap<RSExportType*> ExportTypeMap;
 
@@ -97,14 +101,18 @@
   bool processExportFunc(const clang::FunctionDecl *FD);
   bool processExportType(const llvm::StringRef &Name);
 
-  void cleanupForEach();
+  bool addForEach(const clang::FunctionDecl* FD);
+  int getForEachSlotNumber(const clang::StringRef& funcName);
 
   ExportVarList mExportVars;
   ExportFuncList mExportFuncs;
-  ExportForEachList mExportForEach;
+  std::map<llvm::StringRef, unsigned> mExportForEachMap;
+  ExportForEachVector mExportForEach;
   ExportReduceList mExportReduce;
   ExportTypeMap mExportTypes;
 
+  clang::QualType mAllocationType;
+
  public:
   RSContext(clang::Preprocessor &PP,
             clang::ASTContext &Ctx,
@@ -159,6 +167,12 @@
 
   inline const std::string &getRSPackageName() const { return mRSPackageName; }
 
+  void addAllocationType(const clang::TypeDecl* TD);
+  inline const clang::QualType& getAllocationType() const {
+    return mAllocationType;
+  }
+
+  bool processExportDecl(const clang::DeclGroupRef& DGR);
   bool processExport();
   inline void newExportable(RSExportable *E) {
     if (E != nullptr)
@@ -192,7 +206,7 @@
   }
   inline bool hasExportFunc() const { return !mExportFuncs.empty(); }
 
-  typedef ExportForEachList::const_iterator const_export_foreach_iterator;
+  typedef ExportForEachVector::const_iterator const_export_foreach_iterator;
   const_export_foreach_iterator export_foreach_begin() const {
     return mExportForEach.begin();
   }
@@ -200,6 +214,7 @@
     return mExportForEach.end();
   }
   inline bool hasExportForEach() const { return !mExportForEach.empty(); }
+  int getForEachSlotNumber(const clang::FunctionDecl* FD);
 
   typedef ExportReduceList::const_iterator const_export_reduce_iterator;
   const_export_reduce_iterator export_reduce_begin() const {
diff --git a/slang_rs_foreach_lowering.cpp b/slang_rs_foreach_lowering.cpp
new file mode 100644
index 0000000..ab32c43
--- /dev/null
+++ b/slang_rs_foreach_lowering.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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_foreach_lowering.h"
+
+#include "clang/AST/ASTContext.h"
+#include "llvm/Support/raw_ostream.h"
+#include "slang_rs_context.h"
+#include "slang_rs_export_foreach.h"
+
+namespace slang {
+
+namespace {
+
+const char KERNEL_LAUNCH_FUNCTION_NAME[] = "rsParallelFor";
+const char INTERNAL_LAUNCH_FUNCTION_NAME[] =
+    "_Z17rsForEachInternali13rs_allocationS_";
+
+}  // anonymous namespace
+
+RSForEachLowering::RSForEachLowering(RSContext* ctxt)
+    : mCtxt(ctxt), mASTCtxt(ctxt->getASTContext()) {}
+
+// Check if the passed-in expr references a kernel function in the following
+// pattern in the AST.
+//
+// ImplicitCastExpr 'void *' <BitCast>
+//  `-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay>
+//    `-DeclRefExpr 'int (int)' Function 'foo' 'int (int)'
+const clang::FunctionDecl* RSForEachLowering::matchFunctionDesignator(
+    clang::Expr* expr) {
+  clang::ImplicitCastExpr* ToVoidPtr =
+      clang::dyn_cast<clang::ImplicitCastExpr>(expr);
+  if (ToVoidPtr == nullptr) {
+    return nullptr;
+  }
+
+  clang::ImplicitCastExpr* Decay =
+      clang::dyn_cast<clang::ImplicitCastExpr>(ToVoidPtr->getSubExpr());
+
+  if (Decay == nullptr) {
+    return nullptr;
+  }
+
+  clang::DeclRefExpr* DRE =
+      clang::dyn_cast<clang::DeclRefExpr>(Decay->getSubExpr());
+
+  if (DRE == nullptr) {
+    return nullptr;
+  }
+
+  const clang::FunctionDecl* FD =
+      clang::dyn_cast<clang::FunctionDecl>(DRE->getDecl());
+
+  if (FD == nullptr) {
+    return nullptr;
+  }
+
+  // TODO: Verify the launch has the expected number of input allocations
+
+  return FD;
+}
+
+// Checks if the call expression is a legal rsParallelFor call by looking for the
+// following pattern in the AST. On success, returns the first argument that is
+// a FunctionDecl of a kernel function.
+//
+// CallExpr 'void'
+// |
+// |-ImplicitCastExpr 'void (*)(void *, ...)' <FunctionToPointerDecay>
+// | `-DeclRefExpr  'void (void *, ...)'  'rsParallelFor' 'void (void *, ...)'
+// |
+// |-ImplicitCastExpr 'void *' <BitCast>
+// | `-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay>
+// |   `-DeclRefExpr 'int (int)' Function 'foo' 'int (int)'
+// |
+// |-ImplicitCastExpr 'rs_allocation':'rs_allocation' <LValueToRValue>
+// | `-DeclRefExpr 'rs_allocation':'rs_allocation' lvalue ParmVar 'in' 'rs_allocation':'rs_allocation'
+// |
+// `-ImplicitCastExpr 'rs_allocation':'rs_allocation' <LValueToRValue>
+//   `-DeclRefExpr  'rs_allocation':'rs_allocation' lvalue ParmVar 'out' 'rs_allocation':'rs_allocation'
+const clang::FunctionDecl* RSForEachLowering::matchKernelLaunchCall(
+    clang::CallExpr* CE) {
+  const clang::Decl* D = CE->getCalleeDecl();
+  const clang::FunctionDecl* FD = clang::dyn_cast<clang::FunctionDecl>(D);
+
+  if (FD == nullptr) {
+    return nullptr;
+  }
+
+  const clang::StringRef& funcName = FD->getName();
+
+  if (!funcName.equals(KERNEL_LAUNCH_FUNCTION_NAME)) {
+    return nullptr;
+  }
+
+  const clang::FunctionDecl* kernel = matchFunctionDesignator(CE->getArg(0));
+
+  if (kernel == nullptr ||
+      CE->getNumArgs() < 3) {  // TODO: Make argument check more accurate
+    mCtxt->ReportError(CE->getExprLoc(), "Invalid kernel launch call.");
+  }
+
+  return kernel;
+}
+
+// Create an AST node for the declaration of rsForEachInternal
+clang::FunctionDecl* RSForEachLowering::CreateForEachInternalFunctionDecl() {
+  const clang::QualType& AllocTy = mCtxt->getAllocationType();
+  clang::DeclContext* DC = mASTCtxt.getTranslationUnitDecl();
+  clang::SourceLocation Loc;
+
+  llvm::StringRef SR(INTERNAL_LAUNCH_FUNCTION_NAME);
+  clang::IdentifierInfo& II = mASTCtxt.Idents.get(SR);
+  clang::DeclarationName N(&II);
+
+  clang::FunctionProtoType::ExtProtoInfo EPI;
+
+  clang::QualType T = mASTCtxt.getFunctionType(
+      mASTCtxt.VoidTy,                     // Return type
+      {mASTCtxt.IntTy, AllocTy, AllocTy},  // Argument types
+      EPI);
+
+  clang::FunctionDecl* FD = clang::FunctionDecl::Create(
+      mASTCtxt, DC, Loc, Loc, N, T, nullptr, clang::SC_Extern);
+  return FD;
+}
+
+// Create an expression like the following that references the rsForEachInternal to
+// replace the callee in the original call expression that references rsParallelFor.
+//
+// ImplicitCastExpr 'void (*)(int, rs_allocation, rs_allocation)' <FunctionToPointerDecay>
+// `-DeclRefExpr 'void' Function '_Z17rsForEachInternali13rs_allocationS_' 'void (int, rs_allocation, rs_allocation)'
+clang::Expr* RSForEachLowering::CreateCalleeExprForInternalForEach() {
+  clang::FunctionDecl* FDNew = CreateForEachInternalFunctionDecl();
+
+  clang::DeclRefExpr* refExpr = clang::DeclRefExpr::Create(
+      mASTCtxt, clang::NestedNameSpecifierLoc(), clang::SourceLocation(), FDNew,
+      false, clang::SourceLocation(), mASTCtxt.VoidTy, clang::VK_RValue);
+
+  const clang::QualType FDNewType = FDNew->getType();
+
+  clang::Expr* calleeNew = clang::ImplicitCastExpr::Create(
+      mASTCtxt, mASTCtxt.getPointerType(FDNewType),
+      clang::CK_FunctionToPointerDecay, refExpr, nullptr, clang::VK_RValue);
+
+  return calleeNew;
+}
+
+// This visit method checks (via pattern matching) if the call expression is to
+// rsParallelFor, and the arguments satisfy the restrictions on the
+// rsParallelFor API. If so, replace the call with a rsForEachInternal call
+// with the first argument replaced by the slot number of the kernel function
+// referenced in the original first argument.
+//
+// See comments to the helper methods defined above for details.
+void RSForEachLowering::VisitCallExpr(clang::CallExpr* CE) {
+  const clang::FunctionDecl* kernel = matchKernelLaunchCall(CE);
+  if (kernel == nullptr) {
+    return;
+  }
+
+  clang::Expr* calleeNew = CreateCalleeExprForInternalForEach();
+  CE->setCallee(calleeNew);
+
+  const int slot = mCtxt->getForEachSlotNumber(kernel);
+  const llvm::APInt APIntSlot(mASTCtxt.getTypeSize(mASTCtxt.IntTy), slot);
+  const clang::Expr* arg0 = CE->getArg(0);
+  const clang::SourceLocation Loc(arg0->getLocStart());
+  clang::Expr* IntSlotNum =
+      clang::IntegerLiteral::Create(mASTCtxt, APIntSlot, mASTCtxt.IntTy, Loc);
+  CE->setArg(0, IntSlotNum);
+}
+
+void RSForEachLowering::VisitStmt(clang::Stmt* S) {
+  for (clang::Stmt* Child : S->children()) {
+    if (Child) {
+      Visit(Child);
+    }
+  }
+}
+
+}  // namespace slang
diff --git a/slang_rs_foreach_lowering.h b/slang_rs_foreach_lowering.h
new file mode 100644
index 0000000..6cb0e62
--- /dev/null
+++ b/slang_rs_foreach_lowering.h
@@ -0,0 +1,52 @@
+/*
+ * 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_RS_FOREACH_LOWERING_H
+#define _FRAMEWORKS_COMPILE_SLANG_RS_FOREACH_LOWERING_H
+
+#include "clang/AST/StmtVisitor.h"
+
+namespace clang {
+  class ASTContext;
+  class CallExpr;
+  class Expr;
+  class FunctionDecl;
+}
+
+namespace slang {
+
+class RSContext;
+
+class RSForEachLowering : public clang::StmtVisitor<RSForEachLowering> {
+ public:
+  RSForEachLowering(RSContext* ctxt);
+
+  void VisitCallExpr(clang::CallExpr *CE);
+  void VisitStmt(clang::Stmt *S);
+
+ private:
+  RSContext* mCtxt;
+  clang::ASTContext& mASTCtxt;
+
+  const clang::FunctionDecl* matchFunctionDesignator(clang::Expr* expr);
+  const clang::FunctionDecl* matchKernelLaunchCall(clang::CallExpr* CE);
+  clang::FunctionDecl* CreateForEachInternalFunctionDecl();
+  clang::Expr* CreateCalleeExprForInternalForEach();
+};  // RSForEachLowering
+
+}  // namespace slang
+
+#endif  // _FRAMEWORKS_COMPILE_SLANG_RS_FOREACH_LOWERING_H