Strip non-kernel functions from RS2SPIRV compilation

Added a pass to trim out non-kernel functions from the rest of SPIR-V
compilation passes. Also changed/enforced the pass ordering so that dead
globals exposed by this pass are removed prior of SPIR-V compilation.

Bug: 30964317

Test: RSoV, RSTest, RSoV LIT tests on Angler
Change-Id: I689758ae0977bca694341e948842b668f68caac8
diff --git a/rsov/compiler/Android.mk b/rsov/compiler/Android.mk
index 3e9eeb7..de86a0b 100644
--- a/rsov/compiler/Android.mk
+++ b/rsov/compiler/Android.mk
@@ -29,6 +29,7 @@
   KernelSignature.cpp \
   LinkerModule.cpp \
   ReflectionPass.cpp \
+  RemoveNonkernelsPass.cpp \
   RSAllocationUtils.cpp \
   RSSPIRVWriter.cpp \
   unit_tests/LinkerModuleTests.cpp
diff --git a/rsov/compiler/RSSPIRVWriter.cpp b/rsov/compiler/RSSPIRVWriter.cpp
index 000fc41..a23e97e 100644
--- a/rsov/compiler/RSSPIRVWriter.cpp
+++ b/rsov/compiler/RSSPIRVWriter.cpp
@@ -33,6 +33,7 @@
 #include "InlinePreparationPass.h"
 #include "LinkerModule.h"
 #include "ReflectionPass.h"
+#include "RemoveNonkernelsPass.h"
 
 #include <fstream>
 #include <sstream>
@@ -83,7 +84,17 @@
   M.setTargetTriple(NewTriple);
 }
 
-void addPassesForRS2SPIRV(llvm::legacy::PassManager &PassMgr) {
+void addPassesForRS2SPIRV(llvm::legacy::PassManager &PassMgr,
+                          bcinfo::MetadataExtractor &Extractor) {
+  PassMgr.add(createInlinePreparationPass(Extractor));
+  PassMgr.add(createAlwaysInlinerPass());
+  PassMgr.add(createRemoveNonkernelsPass(Extractor));
+  // Delete unreachable globals.
+  PassMgr.add(createGlobalDCEPass());
+  // Remove dead debug info.
+  PassMgr.add(createStripDeadDebugInfoPass());
+  // Remove dead func decls.
+  PassMgr.add(createStripDeadPrototypesPass());
   PassMgr.add(createGlobalMergePass());
   PassMgr.add(createPromoteMemoryToRegisterPass());
   PassMgr.add(createTransOCLMD());
@@ -92,7 +103,6 @@
   PassMgr.add(createSPIRVRegularizeLLVM());
   PassMgr.add(createSPIRVLowerConstExpr());
   PassMgr.add(createSPIRVLowerBool());
-  PassMgr.add(createAlwaysInlinerPass());
 }
 
 bool WriteSPIRV(Module *M, llvm::raw_ostream &OS, std::string &ErrMsg) {
@@ -108,8 +118,7 @@
   DEBUG(dbgs() << "Metadata extracted\n");
 
   llvm::legacy::PassManager PassMgr;
-  PassMgr.add(createInlinePreparationPass(ME));
-  addPassesForRS2SPIRV(PassMgr);
+  addPassesForRS2SPIRV(PassMgr, ME);
 
   std::ofstream WrapperF;
   if (!WrapperOutputFile.empty()) {
diff --git a/rsov/compiler/RemoveNonkernelsPass.cpp b/rsov/compiler/RemoveNonkernelsPass.cpp
new file mode 100644
index 0000000..fb2d9ea
--- /dev/null
+++ b/rsov/compiler/RemoveNonkernelsPass.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017, 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 "RemoveNonkernelsPass.h"
+
+#include "bcinfo/MetadataExtractor.h"
+
+#include "llvm/ADT/StringSet.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "rs2spirv-remove"
+
+using namespace llvm;
+
+namespace rs2spirv {
+
+namespace {
+
+class RemoveNonkernelsPass : public ModulePass {
+  bcinfo::MetadataExtractor &ME;
+
+public:
+  static char ID;
+  explicit RemoveNonkernelsPass(bcinfo::MetadataExtractor &Extractor)
+      : ModulePass(ID), ME(Extractor) {}
+
+  const char *getPassName() const override { return "RemoveNonkernelsPass"; }
+
+  bool runOnModule(Module &M) override {
+    DEBUG(dbgs() << "RemoveNonkernelsPass\n");
+    DEBUG(M.dump());
+
+    const size_t RSKernelNum = ME.getExportForEachSignatureCount();
+    const char **RSKernelNames = ME.getExportForEachNameList();
+    if (RSKernelNum == 0)
+      DEBUG(dbgs() << "RemoveNonkernelsPass detected no kernel\n");
+
+    StringSet<> KNames;
+    for (size_t i = 0; i < RSKernelNum; ++i)
+      KNames.insert(RSKernelNames[i]);
+
+    std::vector<Function *> Functions;
+    for (auto &F : M.functions()) {
+      Functions.push_back(&F);
+    }
+
+    for (auto &F : Functions) {
+      if (F->isDeclaration())
+        continue;
+
+      const StringRef FName = F->getName();
+
+      if (KNames.count(FName) != 0)
+        continue; // Skip kernels.
+
+      F->replaceAllUsesWith(UndefValue::get((Type *)F->getType()));
+      F->eraseFromParent();
+
+      DEBUG(dbgs() << "Removed:\t" << FName << '\n');
+    }
+
+    // Return true, as the pass modifies module.
+    DEBUG(M.dump());
+    DEBUG(dbgs() << "Done removal\n");
+
+    return true;
+  }
+};
+}
+
+char RemoveNonkernelsPass::ID = 0;
+
+ModulePass *createRemoveNonkernelsPass(bcinfo::MetadataExtractor &ME) {
+  return new RemoveNonkernelsPass(ME);
+}
+
+} // namespace rs2spirv
diff --git a/rsov/compiler/RemoveNonkernelsPass.h b/rsov/compiler/RemoveNonkernelsPass.h
new file mode 100644
index 0000000..ec2a561
--- /dev/null
+++ b/rsov/compiler/RemoveNonkernelsPass.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017, 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 RS2SPIRV_REMOVE_NONKERNELS_PASS_H
+#define RS2SPIRV_REMOVE_NONKERNELS_PASS_H
+
+namespace llvm {
+class ModulePass;
+}
+
+namespace bcinfo {
+class MetadataExtractor;
+}
+
+namespace rs2spirv {
+
+llvm::ModulePass *
+createRemoveNonkernelsPass(bcinfo::MetadataExtractor &Extractor);
+
+} // namespace rs2spirv
+
+#endif
diff --git a/rsov/compiler/tests/single_kernel/mixed.ll b/rsov/compiler/tests/single_kernel/mixed.ll
new file mode 100644
index 0000000..7000ceb
--- /dev/null
+++ b/rsov/compiler/tests/single_kernel/mixed.ll
@@ -0,0 +1,66 @@
+; RUN: rs2spirv_lit_driver.sh %s | FileCheck %s
+; CHECK: OpEntryPoint GLCompute %__rsov_entry_inc "inc" %gl_GlobalInvocationID
+; CHECK-NOT: an_invokable
+
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+@.str = private unnamed_addr constant [24 x i8] c"test_root_output FAILED\00", align 1
+
+; Function Attrs: nounwind
+define void @an_invokable(float %i) local_unnamed_addr #0 {
+entry:
+  %call = tail call fastcc float @sum(float %i, float 2.000000e+00)
+  %conv = fptosi float %call to i32
+  tail call void @_Z7rsDebugPKci(i8* getelementptr inbounds ([24 x i8], [24 x i8]* @.str, i32 0, i32 0), i32 %conv) #4
+  ret void
+}
+
+declare void @_Z7rsDebugPKci(i8*, i32) local_unnamed_addr #1
+
+; Function Attrs: norecurse nounwind readnone
+define internal fastcc float @sum(float %i, float %j) unnamed_addr #2 {
+entry:
+  %add = fadd float %i, %j
+  ret float %add
+}
+
+; Function Attrs: norecurse nounwind readnone
+define float @inc(float %i) local_unnamed_addr #2 {
+entry:
+  %call = tail call fastcc float @sum(float %i, float 1.000000e+00)
+  ret float %call
+}
+
+; Function Attrs: noinline nounwind
+define void @.helper_an_invokable({ float }* nocapture) local_unnamed_addr #3 {
+entry:
+  %1 = getelementptr inbounds { float }, { float }* %0, i32 0, i32 0
+  %2 = load float, float* %1, align 4
+  tail call void @an_invokable(float %2)
+  ret void
+}
+
+attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="0" "stackrealign" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #3 = { noinline nounwind }
+attributes #4 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4}
+!\23rs_export_func = !{!5}
+!\23rs_export_foreach_name = !{!6, !7}
+!\23rs_export_foreach = !{!8, !9}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"Android clang version 3.8.275480  (based on LLVM 3.8.275480)"}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"rs2spirv"}
+!5 = !{!".helper_an_invokable"}
+!6 = !{!"root"}
+!7 = !{!"inc"}
+!8 = !{!"0"}
+!9 = !{!"35"}