Add a -build-checksum flag to bcc

bug 19216507

Add a flag to bcc to pass a build checksum.  The checksum is passed to
RSEmbedInfo pass via LLVM metadata.

The old checksum path is tied to RSInfo.  Removing this path can be done
along with RSInfo removal.

Change-Id: I3f21c96ddcfe42c16754fbb7749a72595f90964d
diff --git a/bcinfo/MetadataExtractor.cpp b/bcinfo/MetadataExtractor.cpp
index 4d868c8..cbf0fbc 100644
--- a/bcinfo/MetadataExtractor.cpp
+++ b/bcinfo/MetadataExtractor.cpp
@@ -62,6 +62,10 @@
 
 static const llvm::StringRef ThreadableMetadataName = "#rs_is_threadable";
 
+// Name of metadata node where the checksum for this build is stored.  (should
+// be synced with libbcc/lib/Core/Source.cpp)
+static const llvm::StringRef ChecksumMetadataName = "#rs_build_checksum";
+
 MetadataExtractor::MetadataExtractor(const char *bitcode, size_t bitcodeSize)
     : mModule(nullptr), mBitcode(bitcode), mBitcodeSize(bitcodeSize),
       mExportVarCount(0), mExportFuncCount(0), mExportForEachSignatureCount(0),
@@ -70,7 +74,7 @@
       mExportForEachInputCountList(nullptr), mPragmaCount(0),
       mPragmaKeyList(nullptr), mPragmaValueList(nullptr), mObjectSlotCount(0),
       mObjectSlotList(nullptr), mRSFloatPrecision(RS_FP_Full),
-      mIsThreadable(true) {
+      mIsThreadable(true), mBuildChecksum(nullptr) {
   BitcodeWrapper wrapper(bitcode, bitcodeSize);
   mCompilerVersion = wrapper.getCompilerVersion();
   mOptimizationLevel = wrapper.getOptimizationLevel();
@@ -85,7 +89,7 @@
       mExportForEachInputCountList(nullptr), mPragmaCount(0),
       mPragmaKeyList(nullptr), mPragmaValueList(nullptr), mObjectSlotCount(0),
       mObjectSlotList(nullptr), mRSFloatPrecision(RS_FP_Full),
-      mIsThreadable(true) {
+      mIsThreadable(true), mBuildChecksum(nullptr) {
   mCompilerVersion = RS_VERSION;  // Default to the actual current version.
   mOptimizationLevel = 3;
 }
@@ -140,6 +144,8 @@
   delete [] mObjectSlotList;
   mObjectSlotList = nullptr;
 
+  delete [] mBuildChecksum;
+
   return;
 }
 
@@ -457,6 +463,22 @@
 
 }
 
+void MetadataExtractor::readBuildChecksumMetadata(
+    const llvm::NamedMDNode *ChecksumMetadata) {
+
+  if (ChecksumMetadata == nullptr)
+    return;
+
+  llvm::MDNode *mdNode = ChecksumMetadata ->getOperand(0);
+  if (mdNode == nullptr)
+    return;
+
+  llvm::Value *mdValue = mdNode->getOperand(0);
+  if (mdValue == nullptr)
+    return;
+
+  mBuildChecksum = createStringFromValue(mdValue);
+}
 
 bool MetadataExtractor::extract() {
   if (!(mBitcode && mBitcodeSize) && !mModule) {
@@ -498,6 +520,8 @@
       mModule->getNamedMetadata(ObjectSlotMetadataName);
   const llvm::NamedMDNode *ThreadableMetadata =
       mModule->getNamedMetadata(ThreadableMetadataName);
+  const llvm::NamedMDNode *ChecksumMetadata =
+      mModule->getNamedMetadata(ChecksumMetadataName);
 
 
   if (!populateVarNameMetadata(ExportVarMetadata)) {
@@ -524,6 +548,7 @@
   }
 
   readThreadableFlag(ThreadableMetadata);
+  readBuildChecksumMetadata(ChecksumMetadata);
 
   return true;
 }
diff --git a/include/bcc/Renderscript/RSCompilerDriver.h b/include/bcc/Renderscript/RSCompilerDriver.h
index f56f7eb..efc1fa4 100644
--- a/include/bcc/Renderscript/RSCompilerDriver.h
+++ b/include/bcc/Renderscript/RSCompilerDriver.h
@@ -66,9 +66,12 @@
   //   invalidation decision.
   // - If pDumpIR is true, a ".ll" file will also be created.
   Compiler::ErrorCode compileScript(RSScript& pScript, const char* pScriptName,
-                                    const char* pOutputPath, const char* pRuntimePath,
+                                    const char* pOutputPath,
+                                    const char *pRuntimePath,
                                     const RSInfo::DependencyHashTy& pSourceHash,
-                                    const char* commandLineToEmbed, bool saveInfoFile, bool pDumpIR);
+                                    const char* commandLineToEmbed,
+                                    const char* pBuildChecksum,
+                                    bool saveInfoFile, bool pDumpIR);
 
 public:
   RSCompilerDriver(bool pUseCompilerRT = true);
@@ -111,7 +114,7 @@
   // Returns true if script is successfully compiled.
   bool build(BCCContext& pContext, const char* pCacheDir, const char* pResName,
              const char* pBitcode, size_t pBitcodeSize, const char* commandLine,
-             const char* pRuntimePath,
+             const char *pBuildChecksum, const char* pRuntimePath,
              RSLinkRuntimeCallback pLinkRuntimeCallback = nullptr,
              bool pDumpIR = false);
 
@@ -121,7 +124,9 @@
       bool dumpIR);
 
   // Returns true if script is successfully compiled.
-  bool buildForCompatLib(RSScript &pScript, const char *pOut, const char *pRuntimePath, bool pDumpIR);
+  bool buildForCompatLib(RSScript &pScript, const char *pOut,
+                         const char *pBuildChecksum, const char *pRuntimePath,
+                         bool pDumpIR);
 };
 
 } // end namespace bcc
diff --git a/include/bcc/Source.h b/include/bcc/Source.h
index 982dbe8..54263a2 100644
--- a/include/bcc/Source.h
+++ b/include/bcc/Source.h
@@ -80,6 +80,8 @@
   // when it's created using CreateFromBuffer and pPath if CreateFromFile().
   const std::string &getIdentifier() const;
 
+  void addBuildChecksumMetadata(const char *) const;
+
   ~Source();
 };
 
diff --git a/include/bcinfo/MetadataExtractor.h b/include/bcinfo/MetadataExtractor.h
index 65920f2..6c44962 100644
--- a/include/bcinfo/MetadataExtractor.h
+++ b/include/bcinfo/MetadataExtractor.h
@@ -64,6 +64,8 @@
   // Flag to mark that script is threadable.  True by default.
   bool mIsThreadable;
 
+  const char *mBuildChecksum;
+
   // Helper functions for extraction
   bool populateVarNameMetadata(const llvm::NamedMDNode *VarNameMetadata);
   bool populateFuncNameMetadata(const llvm::NamedMDNode *FuncNameMetadata);
@@ -72,6 +74,7 @@
   bool populateObjectSlotMetadata(const llvm::NamedMDNode *ObjectSlotMetadata);
   void populatePragmaMetadata(const llvm::NamedMDNode *PragmaMetadata);
   void readThreadableFlag(const llvm::NamedMDNode *ThreadableMetadata);
+  void readBuildChecksumMetadata(const llvm::NamedMDNode *ChecksumMetadata);
 
   uint32_t calculateNumInputs(const llvm::Function *Function,
                               uint32_t Signature);
@@ -283,6 +286,12 @@
     return mIsThreadable;
   }
 
+  /**
+   * \return the build checksum extracted from the LLVM metadata
+   */
+  const char *getBuildChecksum() const {
+    return mBuildChecksum;
+  }
 };
 
 }  // namespace bcinfo
diff --git a/lib/Core/Source.cpp b/lib/Core/Source.cpp
index e7b3781..94da98e 100644
--- a/lib/Core/Source.cpp
+++ b/lib/Core/Source.cpp
@@ -173,4 +173,12 @@
   return mModule->getModuleIdentifier();
 }
 
+void Source::addBuildChecksumMetadata(const char *buildChecksum) const {
+    llvm::LLVMContext &context = mContext.mImpl->mLLVMContext;
+    llvm::MDString *val = llvm::MDString::get(context, buildChecksum);
+    llvm::NamedMDNode *node =
+        mModule->getOrInsertNamedMetadata("#rs_build_checksum");
+    node->addOperand(llvm::MDNode::get(context, val));
+}
+
 } // namespace bcc
diff --git a/lib/Renderscript/RSCompilerDriver.cpp b/lib/Renderscript/RSCompilerDriver.cpp
index 72dfc40..547700e 100644
--- a/lib/Renderscript/RSCompilerDriver.cpp
+++ b/lib/Renderscript/RSCompilerDriver.cpp
@@ -119,8 +119,15 @@
                                                     const char* pRuntimePath,
                                                     const RSInfo::DependencyHashTy& pSourceHash,
                                                     const char* compileCommandLineToEmbed,
+                                                    const char* pBuildChecksum,
                                                     bool saveInfoFile, bool pDumpIR) {
   // android::StopWatch compile_time("bcc: RSCompilerDriver::compileScript time");
+
+  // embed build checksum metadata into the source
+  if (pBuildChecksum != nullptr && strlen(pBuildChecksum) > 0) {
+    pScript.getSource().addBuildChecksumMetadata(pBuildChecksum);
+  }
+
   RSInfo *info = nullptr;
 
   //===--------------------------------------------------------------------===//
@@ -251,6 +258,7 @@
                              const char *pBitcode,
                              size_t pBitcodeSize,
                              const char *commandLine,
+                             const char *pBuildChecksum,
                              const char *pRuntimePath,
                              RSLinkRuntimeCallback pLinkRuntimeCallback,
                              bool pDumpIR) {
@@ -313,7 +321,7 @@
   Compiler::ErrorCode status = compileScript(script, pResName,
                                              output_path.c_str(),
                                              pRuntimePath, bitcode_sha1, commandLine,
-                                             true, pDumpIR);
+                                             pBuildChecksum, true, pDumpIR);
 
   return status == Compiler::kSuccess;
 }
@@ -333,17 +341,20 @@
 
   uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH];
   const char* compileCommandLineToEmbed = "";
+  const char* buildChecksum = nullptr;
 
   llvm::SmallString<80> output_path(pOutputFilepath);
   llvm::sys::path::replace_extension(output_path, ".o");
 
   compileScript(script, pOutputFilepath, output_path.c_str(), pRuntimePath,
-                bitcode_sha1, compileCommandLineToEmbed, true, dumpIR);
+                bitcode_sha1, compileCommandLineToEmbed, buildChecksum,
+                true, dumpIR);
 
   return true;
 }
 
 bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut,
+                                         const char *pBuildChecksum,
                                          const char *pRuntimePath,
                                          bool pDumpIR) {
   // For compat lib, we don't check the RS info file so we don't need the source hash,
@@ -365,7 +376,7 @@
   pScript.setEmbedInfo(true);
 
   Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath, bitcode_sha1,
-                                             compileCommandLineToEmbed, false, pDumpIR);
+                                             compileCommandLineToEmbed, pBuildChecksum, false, pDumpIR);
   if (status != Compiler::kSuccess) {
     return false;
   }
diff --git a/lib/Renderscript/RSEmbedInfo.cpp b/lib/Renderscript/RSEmbedInfo.cpp
index 0ae97a2..731e778 100644
--- a/lib/Renderscript/RSEmbedInfo.cpp
+++ b/lib/Renderscript/RSEmbedInfo.cpp
@@ -84,6 +84,8 @@
     const char **pragmaKeyList = me.getPragmaKeyList();
     const char **pragmaValueList = me.getPragmaValueList();
     bool isThreadable = me.isThreadable();
+    const char *buildChecksum = me.getBuildChecksum();
+
     size_t i;
 
     // We use a simple text format here that the compatibility library can
@@ -121,6 +123,10 @@
     }
     s << "isThreadable: " << ((isThreadable) ? "yes" : "no") << "\n";
 
+    if (buildChecksum != nullptr) {
+      s << "buildChecksum: " << buildChecksum << "\n";
+    }
+
     s.flush();
     return str;
   }
diff --git a/tools/bcc/Main.cpp b/tools/bcc/Main.cpp
index c9ac25f..fcc44f2 100644
--- a/tools/bcc/Main.cpp
+++ b/tools/bcc/Main.cpp
@@ -93,6 +93,12 @@
 OptRSDebugContext("rs-debug-ctx",
     llvm::cl::desc("Enable build to work with a RenderScript debug context"));
 
+llvm::cl::opt<std::string>
+OptChecksum("build-checksum",
+            llvm::cl::desc("Embed a checksum of this compiler invocation for"
+                           " cache invalidation at a later time"),
+            llvm::cl::value_desc("checksum"));
+
 //===----------------------------------------------------------------------===//
 // Compiler Options
 //===----------------------------------------------------------------------===//
@@ -282,7 +288,7 @@
 
   if (!OptEmbedRSInfo) {
     bool built = RSCD.build(context, OptOutputPath.c_str(), OptOutputFilename.c_str(), bitcode,
-                            bitcodeSize, commandLine.c_str(), OptBCLibFilename.c_str(), nullptr,
+                            bitcodeSize, commandLine.c_str(), OptChecksum.c_str(), OptBCLibFilename.c_str(), nullptr,
                             OptEmitLLVM);
 
     if (!built) {
@@ -306,7 +312,8 @@
     llvm::sys::path::append(output, "/", OptOutputFilename);
     llvm::sys::path::replace_extension(output, ".o");
 
-    if (!RSCD.buildForCompatLib(*s, output.c_str(), OptBCLibFilename.c_str(), OptEmitLLVM)) {
+    if (!RSCD.buildForCompatLib(*s, output.c_str(), OptChecksum.c_str(),
+                                OptBCLibFilename.c_str(), OptEmitLLVM)) {
       fprintf(stderr, "Failed to compile script!");
       return EXIT_FAILURE;
     }
diff --git a/tools/bcc_compat/Main.cpp b/tools/bcc_compat/Main.cpp
index 7358222..e3445a0 100644
--- a/tools/bcc_compat/Main.cpp
+++ b/tools/bcc_compat/Main.cpp
@@ -268,7 +268,7 @@
 
   RSScript *s = nullptr;
   s = PrepareRSScript(context, OptInputFilenames);
-  if (!rscd.buildForCompatLib(*s, OutputFilename.c_str(), OptRuntimePath.c_str(), false)) {
+  if (!rscd.buildForCompatLib(*s, OutputFilename.c_str(), nullptr, OptRuntimePath.c_str(), false)) {
     fprintf(stderr, "Failed to compile script!");
     return EXIT_FAILURE;
   }