llvm-rs-as: Wrap bitcode with the RS bitcode wrapper.

Change llvm-rs-as and llvm-rs-cc to use the same code path when it comes
to writing bitcode.  As a result llvm-rs-as can take a *.ll file and
output a *.bc file containing the RenderScript bitcode wrapper, so that
code produced by llvm-rs-as can be read by bcinfo.

Change-Id: I3f9aed3764cbafddb219a21abd89490ca8019bc1
diff --git a/Android.mk b/Android.mk
index 55f35e7..4fdd5a5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -81,6 +81,7 @@
 
 LOCAL_SRC_FILES :=	\
 	slang.cpp	\
+	slang_bitcode_gen.cpp	\
 	slang_backend.cpp	\
 	slang_pragma_recorder.cpp	\
 	slang_diagnostic_buffer.cpp
diff --git a/lit-tests/bitcode_wrapper/bitcode_wrapper_test.ll b/lit-tests/bitcode_wrapper/bitcode_wrapper_test.ll
new file mode 100644
index 0000000..77e32c0
--- /dev/null
+++ b/lit-tests/bitcode_wrapper/bitcode_wrapper_test.ll
@@ -0,0 +1,51 @@
+; This test assembles this file to bitcode with all supported target
+; API versions, then checks that the bitcode file was generated and
+; has the right magic number.
+
+; RUN: %llvm-rs-as -target-api 11 %s -o %t11
+; RUN: xxd -ps -l 4 %t11 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 12 %s -o %t12
+; RUN: xxd -ps -l 4 %t12 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 13 %s -o %t13
+; RUN: xxd -ps -l 4 %t13 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 14 %s -o %t14
+; RUN: xxd -ps -l 4 %t14 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 15 %s -o %t15
+; RUN: xxd -ps -l 4 %t15 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 16 %s -o %t16
+; RUN: xxd -ps -l 4 %t16 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 17 %s -o %t17
+; RUN: xxd -ps -l 4 %t17 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 18 %s -o %t18
+; RUN: xxd -ps -l 4 %t18 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 19 %s -o %t19
+; RUN: xxd -ps -l 4 %t19 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 20 %s -o %t20
+; RUN: xxd -ps -l 4 %t20 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 21 %s -o %t21
+; RUN: xxd -ps -l 4 %t21 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 22 %s -o %t22
+; RUN: xxd -ps -l 4 %t22 | FileCheck %s
+; RUN: %llvm-rs-as -target-api 23 %s -o %t23
+; RUN: xxd -ps -l 4 %t23 | FileCheck %s
+
+; RUN: %llvm-rs-as -target-api 0 %s -o %t0
+; RUN: xxd -ps -l 4 %t0 | FileCheck %s
+
+; Check for the magic number.
+
+; CHECK: dec0170b
+
+; ModuleID = 'kernel.bc'
+target datalayout = "e-p:32:32-i64:64-v128:64:128-n32-S64"
+target triple = "armv7-none-linux-gnueabi"
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+!\23pragma = !{!3, !4}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"clang version 3.6 "}
+!3 = !{!"version", !"1"}
+!4 = !{!"java_package_name", !"foo"}
diff --git a/lit-tests/lit.cfg b/lit-tests/lit.cfg
index 23337a1..88ad6e6 100644
--- a/lit-tests/lit.cfg
+++ b/lit-tests/lit.cfg
@@ -6,7 +6,7 @@
 config.name = 'slang_lit_tests'
 
 # suffixes: A list of file extensions to treat as test files.
-config.suffixes = ['.rs']
+config.suffixes = ['.rs', '.ll']
 
 # testFormat: The test format to use to interpret tests.
 import lit.formats
@@ -45,6 +45,7 @@
     return os.path.abspath(tool)
 
 config.slang = inferTool('llvm-rs-cc', 'SLANG', os.path.join(config.base_path, 'out', 'host', 'linux-x86', 'bin')).replace('\\', '/')
+config.llvm_rs_as = inferTool('llvm-rs-as', 'LLVM_RS_AS', os.path.join(config.base_path, 'out', 'host', 'linux-x86', 'bin')).replace('\\', '/')
 
 config.filecheck = inferTool('FileCheck', 'FILECHECK', config.environment['PATH'])
 config.rs_filecheck_wrapper = inferTool('rs-filecheck-wrapper.sh', 'RS_FILECHECK_WRAPPER', os.path.join(config.base_path, 'frameworks', 'compile', 'slang', 'lit-tests'))
@@ -59,10 +60,12 @@
 
 if not lit_config.quiet:
     lit_config.note('using slang: %r' % config.slang)
+    lit_config.note('using llvm-rs-as: %r' % config.llvm_rs_as)
     lit_config.note('using FileCheck: %r' % config.filecheck)
     lit_config.note('using rs-filecheck-wrapper.sh: %r' % config.rs_filecheck_wrapper)
     lit_config.note('using output directory: %r' % config.test_exec_root)
 
 # Tools configuration substitutions
 config.substitutions.append( ('%Slang', ' ' + config.slang + ' ' + config.slang_includes + ' ' + config.slang_options ) )
+config.substitutions.append( ('%llvm-rs-as', config.llvm_rs_as) )
 config.substitutions.append( ('%rs-filecheck-wrapper', ' ' + config.rs_filecheck_wrapper + ' ' + config.test_exec_root + ' ' + config.filecheck + ' ') )
diff --git a/llvm-rs-as.cpp b/llvm-rs-as.cpp
index 1f81b14..c63a1ac 100644
--- a/llvm-rs-as.cpp
+++ b/llvm-rs-as.cpp
@@ -29,9 +29,8 @@
 #include "llvm/Support/SystemUtils.h"
 #include "llvm/Support/ToolOutputFile.h"
 
-#include "BitWriter_3_2/ReaderWriter_3_2.h"
-#include "BitWriter_2_9/ReaderWriter_2_9.h"
-#include "BitWriter_2_9_func/ReaderWriter_2_9_func.h"
+#include "slang_bitcode_gen.h"
+#include "slang_version.h"
 
 #include <memory>
 using namespace llvm;
@@ -49,6 +48,11 @@
 static cl::opt<bool>
 DisableOutput("disable-output", cl::desc("Disable output"), cl::init(false));
 
+static cl::opt<uint32_t>
+TargetAPI("target-api", cl::desc("Specify RenderScript target API version "
+                                 "(0 = development API) (default is 0)"),
+          cl::init(0));
+
 static cl::opt<bool>
 DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden);
 
@@ -56,20 +60,8 @@
 DisableVerify("disable-verify", cl::Hidden,
               cl::desc("Do not run verifier on input LLVM (dangerous!)"));
 
-enum BCVersion {
-  BC29, BC29Func, BC32, BCHEAD
-};
 
-cl::opt<BCVersion> BitcodeVersion("bitcode-version",
-  cl::desc("Set the bitcode version to be written:"),
-  cl::values(
-    clEnumValN(BC29, "BC29", "Version 2.9"),
-     clEnumVal(BC29Func,     "Version 2.9 func"),
-     clEnumVal(BC32,         "Version 3.2"),
-     clEnumVal(BCHEAD,       "Most current version"),
-    clEnumValEnd), cl::init(BC32));
-
-static void WriteOutputFile(const Module *M) {
+static void WriteOutputFile(const Module *M, uint32_t ModuleTargetAPI) {
   // Infer the output filename if needed.
   if (OutputFilename.empty()) {
     if (InputFilename == "-") {
@@ -97,24 +89,15 @@
   }
 
   if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) {
-    switch(BitcodeVersion) {
-      case BC29:
-        llvm_2_9::WriteBitcodeToFile(M, Out->os());
-        break;
-      case BC29Func:
-        llvm_2_9_func::WriteBitcodeToFile(M, Out->os());
-        break;
-      case BC32:
-        llvm_3_2::WriteBitcodeToFile(M, Out->os());
-        break;
-      case BCHEAD:
-        llvm::WriteBitcodeToFile(M, Out->os());
-        break;
+    slang::writeBitcode(Out->os(), *M,
+        /* TargetAPI = */ ModuleTargetAPI,
+        /* OptimizationLevel = */ 3);
+
+    if (!Out->os().has_error()) {
+      // Declare success.
+      Out->keep();
     }
   }
-
-  // Declare success.
-  Out->keep();
 }
 
 int main(int argc, char **argv) {
@@ -125,6 +108,18 @@
   llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
   cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n");
 
+  // Check target API.
+  uint32_t ActualTargetAPI = (TargetAPI == 0) ? RS_DEVELOPMENT_API : TargetAPI;
+
+  if (ActualTargetAPI != RS_DEVELOPMENT_API &&
+      (ActualTargetAPI < SLANG_MINIMUM_TARGET_API ||
+       ActualTargetAPI > SLANG_MAXIMUM_TARGET_API)) {
+    errs() << "target API level '" << ActualTargetAPI << "' is out of range "
+           << "('" << SLANG_MINIMUM_TARGET_API << "' - '"
+           << SLANG_MAXIMUM_TARGET_API << "')\n";
+    return 1;
+  }
+
   // Parse the file now...
   SMDiagnostic Err;
   std::unique_ptr<Module> M(parseAssemblyFile(InputFilename, Err, Context));
@@ -147,7 +142,7 @@
   if (DumpAsm) errs() << "Here's the assembly:\n" << *M.get();
 
   if (!DisableOutput)
-    WriteOutputFile(M.get());
+    WriteOutputFile(M.get(), ActualTargetAPI);
 
   return 0;
 }
diff --git a/slang_backend.cpp b/slang_backend.cpp
index ff41367..79cd783 100644
--- a/slang_backend.cpp
+++ b/slang_backend.cpp
@@ -19,8 +19,6 @@
 #include <string>
 #include <vector>
 
-#include "bcinfo/BitcodeWrapper.h"
-
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclGroup.h"
@@ -64,6 +62,7 @@
 
 #include "slang_assert.h"
 #include "slang.h"
+#include "slang_bitcode_gen.h"
 #include "slang_rs_context.h"
 #include "slang_rs_export_foreach.h"
 #include "slang_rs_export_func.h"
@@ -72,9 +71,6 @@
 #include "slang_rs_metadata.h"
 
 #include "strip_unknown_attributes.h"
-#include "BitWriter_2_9/ReaderWriter_2_9.h"
-#include "BitWriter_2_9_func/ReaderWriter_2_9_func.h"
-#include "BitWriter_3_2/ReaderWriter_3_2.h"
 
 namespace slang {
 
@@ -234,22 +230,6 @@
   mpModule = mGen->GetModule();
 }
 
-// Encase the Bitcode in a wrapper containing RS version information.
-void Backend::WrapBitcode(llvm::raw_string_ostream &Bitcode) {
-  bcinfo::AndroidBitcodeWrapper wrapper;
-  size_t actualWrapperLen = bcinfo::writeAndroidBitcodeWrapper(
-      &wrapper, Bitcode.str().length(), getTargetAPI(),
-      SlangVersion::CURRENT, mCodeGenOpts.OptimizationLevel);
-
-  slangAssert(actualWrapperLen > 0);
-
-  // Write out the bitcode wrapper.
-  mBufferOutStream.write(reinterpret_cast<char*>(&wrapper), actualWrapperLen);
-
-  // Write out the actual encoded bitcode.
-  mBufferOutStream << Bitcode.str();
-}
-
 void Backend::HandleTranslationUnit(clang::ASTContext &Ctx) {
   HandleTranslationUnitPre(Ctx);
 
@@ -339,40 +319,8 @@
       break;
     }
     case Slang::OT_Bitcode: {
-      llvm::legacy::PassManager *BCEmitPM = new llvm::legacy::PassManager();
-      std::string BCStr;
-      llvm::raw_string_ostream Bitcode(BCStr);
-      unsigned int TargetAPI = getTargetAPI();
-      switch (TargetAPI) {
-        case SLANG_HC_TARGET_API:
-        case SLANG_HC_MR1_TARGET_API:
-        case SLANG_HC_MR2_TARGET_API: {
-          // Pre-ICS targets must use the LLVM 2.9 BitcodeWriter
-          BCEmitPM->add(llvm_2_9::createBitcodeWriterPass(Bitcode));
-          break;
-        }
-        case SLANG_ICS_TARGET_API:
-        case SLANG_ICS_MR1_TARGET_API: {
-          // ICS targets must use the LLVM 2.9_func BitcodeWriter
-          BCEmitPM->add(llvm_2_9_func::createBitcodeWriterPass(Bitcode));
-          break;
-        }
-        default: {
-          if (TargetAPI != SLANG_DEVELOPMENT_TARGET_API &&
-              (TargetAPI < SLANG_MINIMUM_TARGET_API ||
-               TargetAPI > SLANG_MAXIMUM_TARGET_API)) {
-            slangAssert(false && "Invalid target API value");
-          }
-          // Switch to the 3.2 BitcodeWriter by default, and don't use
-          // LLVM's included BitcodeWriter at all (for now).
-          BCEmitPM->add(llvm_3_2::createBitcodeWriterPass(Bitcode));
-          //BCEmitPM->add(llvm::createBitcodeWriterPass(Bitcode));
-          break;
-        }
-      }
-
-      BCEmitPM->run(*mpModule);
-      WrapBitcode(Bitcode);
+      writeBitcode(mBufferOutStream, *mpModule, getTargetAPI(),
+                   mCodeGenOpts.OptimizationLevel);
       break;
     }
     case Slang::OT_Nothing: {
diff --git a/slang_backend.h b/slang_backend.h
index e3dbdef..c3eb3bc 100644
--- a/slang_backend.h
+++ b/slang_backend.h
@@ -81,8 +81,6 @@
   void CreateModulePasses();
   bool CreateCodeGenPasses();
 
-  void WrapBitcode(llvm::raw_string_ostream &Bitcode);
-
   RSContext *mContext;
 
   clang::SourceManager &mSourceMgr;
diff --git a/slang_bitcode_gen.cpp b/slang_bitcode_gen.cpp
new file mode 100644
index 0000000..83d96bf
--- /dev/null
+++ b/slang_bitcode_gen.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "bcinfo/BitcodeWrapper.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+#include "BitWriter_2_9/ReaderWriter_2_9.h"
+#include "BitWriter_2_9_func/ReaderWriter_2_9_func.h"
+#include "BitWriter_3_2/ReaderWriter_3_2.h"
+
+#include "slang_assert.h"
+#include "slang_bitcode_gen.h"
+#include "slang_version.h"
+
+namespace slang {
+
+void writeBitcode(llvm::raw_ostream &Out,
+                  const llvm::Module &M,
+                  uint32_t TargetAPI,
+                  uint32_t OptimizationLevel) {
+  std::string BitcodeStr;
+  llvm::raw_string_ostream Bitcode(BitcodeStr);
+
+  // Create the bitcode.
+  switch (TargetAPI) {
+  case SLANG_HC_TARGET_API:
+  case SLANG_HC_MR1_TARGET_API:
+  case SLANG_HC_MR2_TARGET_API: {
+    // Pre-ICS targets must use the LLVM 2.9 BitcodeWriter
+    llvm_2_9::WriteBitcodeToFile(&M, Bitcode);
+    break;
+  }
+  case SLANG_ICS_TARGET_API:
+  case SLANG_ICS_MR1_TARGET_API: {
+    // ICS targets must use the LLVM 2.9_func BitcodeWriter
+    llvm_2_9_func::WriteBitcodeToFile(&M, Bitcode);
+    break;
+  }
+  default: {
+    if (TargetAPI != SLANG_DEVELOPMENT_TARGET_API &&
+        (TargetAPI < SLANG_MINIMUM_TARGET_API ||
+         TargetAPI > SLANG_MAXIMUM_TARGET_API)) {
+      slangAssert(false && "Invalid target API value");
+    }
+    // Switch to the 3.2 BitcodeWriter by default, and don't use
+    // LLVM's included BitcodeWriter at all (for now).
+    llvm_3_2::WriteBitcodeToFile(&M, Bitcode);
+    break;
+  }
+  }
+
+  const uint32_t CompilerVersion = SlangVersion::CURRENT;
+
+  // Create the bitcode wrapper.
+  bcinfo::AndroidBitcodeWrapper Wrapper;
+  size_t ActualWrapperLen = bcinfo::writeAndroidBitcodeWrapper(
+        &Wrapper, Bitcode.str().length(), TargetAPI,
+        CompilerVersion, OptimizationLevel);
+
+  slangAssert(ActualWrapperLen > 0);
+
+  // Write out the file.
+  Out.write(reinterpret_cast<char*>(&Wrapper), ActualWrapperLen);
+  Out << Bitcode.str();
+}
+
+}  // namespace slang
diff --git a/slang_bitcode_gen.h b/slang_bitcode_gen.h
new file mode 100644
index 0000000..cc0e9f6
--- /dev/null
+++ b/slang_bitcode_gen.h
@@ -0,0 +1,38 @@
+/*
+ * 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_SLANG_BITCODE_GEN_H_  // NOLINT
+#define _FRAMEWORKS_COMPILE_SLANG_SLANG_BITCODE_GEN_H_
+
+#include <cstdint>
+
+namespace llvm {
+  class raw_ostream;
+  class Module;
+}
+
+namespace slang {
+
+// Write out the LLVM bitcode for a module, encased in a wrapper
+// containing RS version information.
+void writeBitcode(llvm::raw_ostream &Out,
+                  const llvm::Module &M,
+                  uint32_t TargetAPI,
+                  uint32_t OptimizationLevel);
+
+} // end namespace slang
+
+#endif  // _FRAMEWORKS_COMPILE_SLANG_SLANG_BITCODE_GEN_H_  NOLINT