Provide a pass that creates information about global variables in RS.

Bug: 20306487

This change implements RSGlobalInfoPass, which is an optional LLVM pass that
adds several new global variables to a given Module. These variables contain
information about the other RenderScript global variables that are present.

Change-Id: I671013c11c7a528254edad5c66f2858953c0f5c4
diff --git a/lib/Renderscript/Android.mk b/lib/Renderscript/Android.mk
index 47ed2e7..56cae16 100644
--- a/lib/Renderscript/Android.mk
+++ b/lib/Renderscript/Android.mk
@@ -25,6 +25,7 @@
   RSCompilerDriver.cpp \
   RSEmbedInfo.cpp \
   RSForEachExpand.cpp \
+  RSGlobalInfoPass.cpp \
   RSInvariant.cpp \
   RSScript.cpp \
   RSInvokeHelperPass.cpp \
diff --git a/lib/Renderscript/RSCompilerDriver.cpp b/lib/Renderscript/RSCompilerDriver.cpp
index 09e144a..463560c 100644
--- a/lib/Renderscript/RSCompilerDriver.cpp
+++ b/lib/Renderscript/RSCompilerDriver.cpp
@@ -51,7 +51,8 @@
 
 RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) :
     mConfig(nullptr), mCompiler(), mDebugContext(false),
-    mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true) {
+    mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true),
+    mEmbedGlobalInfo(false), mEmbedGlobalInfoSkipConstant(false) {
   init::Initialize();
 }
 
@@ -249,6 +250,9 @@
 
   script.setLinkRuntimeCallback(getLinkRuntimeCallback());
 
+  script.setEmbedGlobalInfo(mEmbedGlobalInfo);
+  script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant);
+
   // Read information from bitcode wrapper.
   bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize);
   script.setCompilerVersion(wrapper.getCompilerVersion());
@@ -337,6 +341,8 @@
   // Embed the info string directly in the ELF
   script.setEmbedInfo(true);
   script.setOptimizationLevel(RSScript::kOptLvl3);
+  script.setEmbedGlobalInfo(mEmbedGlobalInfo);
+  script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant);
 
   llvm::SmallString<80> output_path(pOutputFilepath);
   llvm::sys::path::replace_extension(output_path, ".o");
@@ -365,6 +371,9 @@
   // offline (host) compilation.
   pScript.setEmbedInfo(true);
 
+  pScript.setEmbedGlobalInfo(mEmbedGlobalInfo);
+  pScript.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant);
+
   Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath,
                                              pBuildChecksum, pDumpIR);
   if (status != Compiler::kSuccess) {
diff --git a/lib/Renderscript/RSGlobalInfoPass.cpp b/lib/Renderscript/RSGlobalInfoPass.cpp
new file mode 100644
index 0000000..a4ee386
--- /dev/null
+++ b/lib/Renderscript/RSGlobalInfoPass.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "bcc/Assert.h"
+#include "bcc/Support/Log.h"
+
+#include <llvm/IR/Constant.h>
+#include <llvm/IR/Constants.h>
+#include <llvm/IR/Type.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/Function.h>
+#include <llvm/Pass.h>
+
+#include <sstream>
+#include <vector>
+
+namespace {
+
+static const bool kDebugGlobalInfo = false;
+
+/* RSGlobalInfoPass: Embeds additional information about RenderScript global
+ * variables into the Module. The 4 variables added are specified as follows:
+ * 1) .rs.global_entries
+ *    i32 - int
+ *    Optional number of global variables.
+ * 2) .rs.global_names
+ *    [N * i8*] - const char *[N]
+ *    Optional global variable name info. Each entry corresponds to the name
+ *    of 1 of the N global variables.
+ * 3) .rs.global_addresses
+ *    [N * i8*] - void*[N] or void**
+ *    Optional global variable address info. Each entry corresponds to the
+ *    address of 1 of the N global variables.
+ * 4) .rs.global_sizes
+ *    [N * i32] or [N * i64] - size_t[N]
+ *    Optional global variable size info. Each entry corresponds to the size
+ *    of 1 of the N global variables.
+ */
+class RSGlobalInfoPass: public llvm::ModulePass {
+private:
+
+public:
+  static char ID;
+
+  // If true, we don't include information about immutable global variables
+  // in our various exported data structures.
+  bool mSkipConstants;
+
+  RSGlobalInfoPass(bool pSkipConstants = false)
+    : ModulePass (ID), mSkipConstants(pSkipConstants) {
+  }
+
+  virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+    // This pass does not use any other analysis passes, but it does
+    // add new global variables.
+  }
+
+  bool runOnModule(llvm::Module &M) override {
+    std::vector<llvm::Constant *> GVAddresses;
+    std::vector<llvm::Constant *> GVNames;
+    std::vector<uint32_t> GVSizes32;
+    std::vector<uint64_t> GVSizes64;
+
+    const llvm::DataLayout &DL = M.getDataLayout();
+    const size_t PointerSizeInBits = DL.getPointerSizeInBits();
+
+    bccAssert(PointerSizeInBits == 32 || PointerSizeInBits == 64);
+
+    int GlobalNumber = 0;
+
+    for (auto &GV : M.globals()) {
+      // Skip constant variables if we were configured to do so.
+      if (mSkipConstants && GV.isConstant()) {
+        continue;
+      }
+
+      // In LLVM, an instance of GlobalVariable is actually a Value
+      // corresponding to the address of it.
+      GVAddresses.push_back(&GV);
+
+      // Since these are all global variables, their type is actually a
+      // pointer to the underlying data. We can extract the total underlying
+      // storage size by looking at the first contained type.
+      auto TypeSize = DL.getTypeAllocSize(GV.getType()->getContainedType(0));
+      if (PointerSizeInBits == 32) {
+        GVSizes32.push_back(TypeSize);
+      } else {
+        GVSizes64.push_back(TypeSize);
+      }
+    }
+
+    // Create the new strings for storing the names of the global variables.
+    // This has to be done as a separate pass (over the original global
+    // variables), because these strings are new global variables themselves.
+    for (auto GVA : GVAddresses) {
+      llvm::Constant *C =
+          llvm::ConstantDataArray::getString(M.getContext(), GVA->getName());
+      std::stringstream VarName;
+      VarName << ".rs.name_str_" << GlobalNumber++;
+      llvm::Value *V = M.getOrInsertGlobal(VarName.str(), C->getType());
+      llvm::GlobalVariable *VarAsStr = llvm::dyn_cast<llvm::GlobalVariable>(V);
+      VarAsStr->setInitializer(C);
+      VarAsStr->setConstant(true);
+      VarAsStr->setLinkage(llvm::GlobalValue::PrivateLinkage);
+      VarAsStr->setUnnamedAddr(true);
+      GVNames.push_back(VarAsStr);
+    }
+
+    if (PointerSizeInBits == 32) {
+      bccAssert(GVAddresses.size() == GVSizes32.size());
+      bccAssert(GVSizes64.size() == 0);
+    } else {
+      bccAssert(GVSizes32.size() == 0);
+      bccAssert(GVAddresses.size() == GVSizes64.size());
+    }
+    size_t NumGlobals = GVAddresses.size();
+
+    // i32
+    llvm::Type *Int32Ty = llvm::Type::getInt32Ty(M.getContext());
+
+    // i32 or i64 depending on our actual size_t
+    llvm::Type *SizeTy = llvm::Type::getIntNTy(M.getContext(),
+                                               PointerSizeInBits);
+
+    // i8* - LLVM uses this to represent void* and char*
+    llvm::Type *VoidPtrTy = llvm::Type::getInt8PtrTy(M.getContext());
+
+    // [NumGlobals * i8*]
+    llvm::ArrayType *VoidPtrArrayTy = llvm::ArrayType::get(VoidPtrTy,
+                                                           NumGlobals);
+    // [NumGlobals * i32] or [NumGlobals * i64]
+    llvm::ArrayType *SizeArrayTy = llvm::ArrayType::get(SizeTy, NumGlobals);
+
+    // 1) @.rs.global_entries = constant i32 NumGlobals
+    llvm::Value *V = M.getOrInsertGlobal(".rs.global_entries", Int32Ty);
+    llvm::GlobalVariable *GlobalEntries =
+        llvm::dyn_cast<llvm::GlobalVariable>(V);
+    llvm::Constant *GlobalEntriesInit =
+        llvm::ConstantInt::get(Int32Ty, NumGlobals);
+    GlobalEntries->setInitializer(GlobalEntriesInit);
+    GlobalEntries->setConstant(true);
+
+    // 2) @.rs.global_names = constant [N * i8*] [...]
+    V = M.getOrInsertGlobal(".rs.global_names", VoidPtrArrayTy);
+    llvm::GlobalVariable *GlobalNames =
+        llvm::dyn_cast<llvm::GlobalVariable>(V);
+    llvm::Constant *GlobalNamesInit =
+        llvm::ConstantArray::get(VoidPtrArrayTy, GVNames);
+    GlobalNames->setInitializer(GlobalNamesInit);
+    GlobalNames->setConstant(true);
+
+    // 3) @.rs.global_addresses = constant [N * i8*] [...]
+    V = M.getOrInsertGlobal(".rs.global_addresses", VoidPtrArrayTy);
+    llvm::GlobalVariable *GlobalAddresses =
+        llvm::dyn_cast<llvm::GlobalVariable>(V);
+    llvm::Constant *GlobalAddressesInit =
+        llvm::ConstantArray::get(VoidPtrArrayTy, GVAddresses);
+    GlobalAddresses->setInitializer(GlobalAddressesInit);
+    GlobalAddresses->setConstant(true);
+
+
+    // 4) @.rs.global_sizes = constant [N * i32 or i64] [...]
+    V = M.getOrInsertGlobal(".rs.global_sizes", SizeArrayTy);
+    llvm::GlobalVariable *GlobalSizes =
+        llvm::dyn_cast<llvm::GlobalVariable>(V);
+    llvm::Constant *GlobalSizesInit;
+    if (PointerSizeInBits == 32) {
+      GlobalSizesInit = llvm::ConstantDataArray::get(M.getContext(), GVSizes32);
+    } else {
+      GlobalSizesInit = llvm::ConstantDataArray::get(M.getContext(), GVSizes64);
+    }
+    GlobalSizes->setInitializer(GlobalSizesInit);
+    GlobalSizes->setConstant(true);
+
+    if (kDebugGlobalInfo) {
+      GlobalEntries->dump();
+      GlobalNames->dump();
+      GlobalAddresses->dump();
+      GlobalSizes->dump();
+    }
+
+    // Upon completion, this pass has always modified the Module.
+    return true;
+  }
+};
+
+}
+
+char RSGlobalInfoPass::ID = 0;
+
+static llvm::RegisterPass<RSGlobalInfoPass> X("embed-rs-global-info",
+  "Embed additional information about RenderScript global variables");
+
+namespace bcc {
+
+llvm::ModulePass * createRSGlobalInfoPass(bool pSkipConstants) {
+  return new RSGlobalInfoPass(pSkipConstants);
+}
+
+}
diff --git a/lib/Renderscript/RSScript.cpp b/lib/Renderscript/RSScript.cpp
index 396b47d..90261ed 100644
--- a/lib/Renderscript/RSScript.cpp
+++ b/lib/Renderscript/RSScript.cpp
@@ -51,7 +51,8 @@
 RSScript::RSScript(Source &pSource)
   : Script(pSource), mCompilerVersion(0),
     mOptimizationLevel(kOptLvl3), mLinkRuntimeCallback(nullptr),
-    mEmbedInfo(false) { }
+    mEmbedInfo(false), mEmbedGlobalInfo(false),
+    mEmbedGlobalInfoSkipConstant(false) { }
 
 bool RSScript::doReset() {
   mCompilerVersion = 0;