Inline functions with ptr to Function storage class

Inline a function if its return value or any argument is
a pointer to the Function storage class.  This avoids many
cases of OpPtrAccessChain on Function storage class.

Fixes https://github.com/google/clspv/issues/135
diff --git a/lib/InlineFuncWithPointerToFunctionArgPass.cpp b/lib/InlineFuncWithPointerToFunctionArgPass.cpp
new file mode 100644
index 0000000..951e8ab
--- /dev/null
+++ b/lib/InlineFuncWithPointerToFunctionArgPass.cpp
@@ -0,0 +1,126 @@
+// Copyright 2018 The Clspv Authors. All rights reserved.
+//
+// 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 "llvm/ADT/UniqueVector.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+#include "clspv/AddressSpace.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "inlinefuncwithpointerfunctionarg"
+
+namespace {
+struct InlineFuncWithPointerToFunctionArgPass : public ModulePass {
+  static char ID;
+  InlineFuncWithPointerToFunctionArgPass() : ModulePass(ID) {}
+
+  bool InlineFunctions(Module &M);
+  bool runOnModule(Module &M) override;
+};
+
+// Returns true if |type| is a pointer to Function storage class.
+bool IsPointerToFunctionStorage(Type *type) {
+  if (auto *pointerTy = dyn_cast<PointerType>(type)) {
+    return pointerTy->getAddressSpace() == clspv::AddressSpace::Private;
+  }
+  return false;
+}
+
+// Returns true if |type| is a function whose return type or any of its
+// arguments are pointer-to-Function storage class.
+bool IsProblematicFunctionType(Type *type) {
+  if (auto *funcTy = dyn_cast<FunctionType>(type)) {
+    if (IsPointerToFunctionStorage(funcTy->getReturnType())) {
+      return true;
+    }
+    for (auto *paramTy : funcTy->params()) {
+      if (IsPointerToFunctionStorage(paramTy)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+} // namespace
+
+char InlineFuncWithPointerToFunctionArgPass::ID = 0;
+static RegisterPass<InlineFuncWithPointerToFunctionArgPass>
+    X("InlineFuncWithPointerToFunctionArgPass",
+      "Inline Function with Pointer-to-Function storage Argument Pass");
+
+namespace clspv {
+llvm::ModulePass *createInlineFuncWithPointerToFunctionArgPass() {
+  return new InlineFuncWithPointerToFunctionArgPass();
+}
+} // namespace clspv
+
+bool InlineFuncWithPointerToFunctionArgPass::runOnModule(Module &M) {
+  bool Changed = false;
+
+  // Loop through our inline pass until they stop changing thing.
+  for (bool localChanged = true; localChanged; Changed |= localChanged) {
+    localChanged = false;
+
+    localChanged |= InlineFunctions(M);
+  }
+
+  return Changed;
+}
+
+bool InlineFuncWithPointerToFunctionArgPass::InlineFunctions(Module &M) {
+  bool Changed = false;
+
+  UniqueVector<CallInst *> WorkList;
+  for (Function &F : M) {
+    for (BasicBlock &BB : F) {
+      for (Instruction &I : BB) {
+        if (auto call = dyn_cast<CallInst>(&I)) {
+          if (IsProblematicFunctionType(call->getFunctionType())) {
+            WorkList.insert(call);
+          }
+        }
+      }
+    }
+  }
+
+  for (CallInst *Call : WorkList) {
+    InlineFunctionInfo IFI;
+    CallSite CS(Call);
+    // Disable generation of lifetime intrinsic.
+    Changed |= InlineFunction(CS, IFI, nullptr, false);
+  }
+
+  // Remove dead functions.
+  bool removed;
+  do {
+    removed = false;
+    for (auto &F : M) {
+      if (F.getCallingConv() == CallingConv::SPIR_KERNEL)
+        continue;
+      if (F.use_begin() == F.use_end()) {
+        F.eraseFromParent();
+        removed = true;
+        Changed = true;
+        break;
+      }
+    }
+  } while (removed);
+
+  return Changed;
+}