Merge "Add option to generate reflected C++ code."
diff --git a/RSCCOptions.td b/RSCCOptions.td
index be992f5..ebd7f36 100644
--- a/RSCCOptions.td
+++ b/RSCCOptions.td
@@ -60,6 +60,12 @@
   HelpText<"Build ASTs then convert to LLVM, but emit nothing">;
 }
 
+def emit_g : Flag<"-g">,
+  HelpText<"Emit LLVM Debug Metadata">;
+
+def optimization_level : Separate<"-O">, MetaVarName<"<optimization-level>">,
+  HelpText<"<optimization-level> can be one of '0' or '3' (default)">;
+
 def allow_rs_prefix : Flag<"-allow-rs-prefix">,
   HelpText<"Allow user-defined function prefixed with 'rs'">;
 
diff --git a/lit-tests/README b/lit-tests/README
new file mode 100644
index 0000000..78fea67
--- /dev/null
+++ b/lit-tests/README
@@ -0,0 +1,42 @@
+
+Summary
+=======
+This directory contains tests for Slang that use the 'llvm-lit' testing tool.
+Each testcase is a separate .rs file, and comments inside the testcase are
+used to verify certain strings are present in the output bitcode files.
+
+Prerequisites
+=============
+To run the tests, you must have the android build environment variables
+set (i.e. source build/envsetup.sh; lunch). You must also have on your path:
+- Android version of llvm-lit (currently in libbcc/tests/debuginfo)
+- FileCheck (utility from llvm)
+- llvm-rs-cc (slang frontend compiler)
+
+If you are unable to run the tests, try using the "--debug" option to llvm-lit.
+
+When debugging failures, the "-v" option will print to stdout the details of
+the failure. Note that tests marked as "Expected Fail" (XFAIL) will not print
+failure information, even with -v.
+
+Customizing
+===========
+The tools lit and FileCheck are fairly flexible, and could be used to validate
+more than just emitted bitcode. For example, with some changes to the testcases
+and the helper shell-script rs-filecheck-wrapper.sh, it should be possible to
+write tests that verify the emitted Java code.
+
+Running
+=======
+To execute all the tests from this directory, use the Android llvm-lit tool
+from libbcc:
+$ ../../libbcc/tests/debuginfo/llvm-lit .
+
+The tool can be run from any directory.
+-j controls the number of parallel test executions
+-v enables additional verbosity (useful when examining unexpected failures)
+
+Adding new tests
+================
+To add new tests, just add .rs files to a test directory with similar
+RUN/CHECK directives in comments as the existing tests.
diff --git a/lit-tests/debug_disabled.rs b/lit-tests/debug_disabled.rs
new file mode 100644
index 0000000..4485697
--- /dev/null
+++ b/lit-tests/debug_disabled.rs
@@ -0,0 +1,10 @@
+// RUN: %Slang %s
+// RUN: %rs-filecheck-wrapper %s
+// CHECK-NOT: DW_TAG_subprogram
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const void *usrData,
+          uint32_t x, uint32_t y) {
+}
diff --git a/lit-tests/debug_enabled.rs b/lit-tests/debug_enabled.rs
new file mode 100644
index 0000000..4632744
--- /dev/null
+++ b/lit-tests/debug_enabled.rs
@@ -0,0 +1,10 @@
+// RUN: %Slang -g %s
+// RUN: %rs-filecheck-wrapper %s
+// CHECK: DW_TAG_subprogram
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const void *usrData,
+          uint32_t x, uint32_t y) {
+}
diff --git a/lit-tests/lit.cfg b/lit-tests/lit.cfg
new file mode 100644
index 0000000..1fb7692
--- /dev/null
+++ b/lit-tests/lit.cfg
@@ -0,0 +1,66 @@
+# -*- Python -*-
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'slang_lit_tests'
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.rs']
+
+# testFormat: The test format to use to interpret tests.
+config.test_format = lit.formats.ShTest()
+
+# Get the base build directory for the android source tree from environment.
+config.base_path = os.getenv('ANDROID_BUILD_TOP')
+
+# test_source_root: The path where tests are located (default is the test suite
+# root).
+config.test_source_root = None
+
+# test_exec_root: The path where tests are located (default is the test suite
+# root).
+config.test_exec_root = os.path.join(config.base_path, 'out', 'tests', 'slang', 'lit-tests')
+
+# target_triple: Used by ShTest and TclTest formats for XFAIL checks.
+config.target_triple = 'slang'
+
+def inferTool(binary_name, env_var, PATH):
+    # Determine which tool to use.
+    tool = os.getenv(env_var)
+
+    # If the user set the overriding environment variable, use it
+    if tool and os.path.isfile(tool):
+        return tool
+
+    # Otherwise look in the path.
+    tool = lit.util.which(binary_name, PATH)
+
+    if not tool:
+        lit.fatal("couldn't find " + binary_name + " program in " + PATH + " , try setting "
+                  + env_var + " in your environment")
+
+    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.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'))
+
+# TODO: fix up for other SDKs. Maybe use lit parameter?
+config.slang_includes = "-I " + os.path.join(config.base_path, 'prebuilt', 'sdk', '14', 'renderscript', 'include') + " " \
+                     +  "-I " + os.path.join(config.base_path, 'prebuilt', 'sdk', '14', 'renderscript', 'clang-include')
+
+config.slang_options = "-emit-llvm -o " + config.test_exec_root \
+                     + " -output-dep-dir " + config.test_exec_root \
+                     + " -java-reflection-path-base " + config.test_exec_root
+
+if not lit.quiet:
+    lit.note('using slang: %r' % config.slang)
+    lit.note('using FileCheck: %r' % config.filecheck)
+    lit.note('using rs-filecheck-wrapper.sh: %r' % config.rs_filecheck_wrapper)
+    lit.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( ('%rs-filecheck-wrapper', ' ' + config.rs_filecheck_wrapper + ' ' + config.test_exec_root + ' ' + config.filecheck + ' ') )
diff --git a/lit-tests/locals_opt_0.rs b/lit-tests/locals_opt_0.rs
new file mode 100644
index 0000000..1c1f7de
--- /dev/null
+++ b/lit-tests/locals_opt_0.rs
@@ -0,0 +1,45 @@
+// RUN: %Slang -O 0 %s
+// RUN: %rs-filecheck-wrapper %s
+// CHECK: define internal i32 @main(
+// CHECK: %f = alloca float,
+// CHECK: %pf = alloca float*,
+// CHECK: %ppn = alloca i32**,
+
+struct float_struct {
+  float f;
+  float f2[2];
+} compound_float;
+
+static
+int main(int argc, char* argv[])
+{
+  float f = 0.f;
+  float *pf = &f;
+
+  double d[2][2] = {{0, 1}, {2, 3.0}};
+  struct float_struct s;
+
+  unsigned short us = -1;
+  const unsigned long l = (unsigned long) -1.0e8f;
+
+  {
+    int** ppn = 0;
+    if (ppn) {
+      return -1;
+    }
+  }
+
+  s.f = 10e-4f;
+  s.f2[0] = 1e4f;
+  s.f2[1] = 100.5f;
+
+  double result = pf[0] * d[1][1] * s.f * us * l;
+  return (result == 0 ? 0 : -1);
+}
+
+void the_main() {
+  main(0, 0);
+}
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
diff --git a/lit-tests/locals_opt_3.rs b/lit-tests/locals_opt_3.rs
new file mode 100644
index 0000000..e90a538
--- /dev/null
+++ b/lit-tests/locals_opt_3.rs
@@ -0,0 +1,45 @@
+// RUN: %Slang -O 3 %s
+// RUN: %rs-filecheck-wrapper %s
+// CHECK-NOT: define internal i32 @main(
+// CHECK-NOT: %f = alloca float,
+// CHECK-NOT: %pf = alloca float*,
+// CHECK-NOT: %ppn = alloca i32**,
+
+struct float_struct {
+  float f;
+  float f2[2];
+} compound_float;
+
+static
+int main(int argc, char* argv[])
+{
+  float f = 0.f;
+  float *pf = &f;
+
+  double d[2][2] = {{0, 1}, {2, 3.0}};
+  struct float_struct s;
+
+  unsigned short us = -1;
+  const unsigned long l = (unsigned long) -1.0e8f;
+
+  {
+    int** ppn = 0;
+    if (ppn) {
+      return -1;
+    }
+  }
+
+  s.f = 10e-4f;
+  s.f2[0] = 1e4f;
+  s.f2[1] = 100.5f;
+
+  double result = pf[0] * d[1][1] * s.f * us * l;
+  return (result == 0 ? 0 : -1);
+}
+
+void the_main() {
+  main(0, 0);
+}
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
diff --git a/lit-tests/locals_opt_default.rs b/lit-tests/locals_opt_default.rs
new file mode 100644
index 0000000..f491956
--- /dev/null
+++ b/lit-tests/locals_opt_default.rs
@@ -0,0 +1,48 @@
+// RUN: %Slang %s
+// RUN: %rs-filecheck-wrapper %s
+// CHECK-NOT: define internal i32 @main(
+// CHECK-NOT: %f = alloca float,
+// CHECK-NOT: %pf = alloca float*,
+// CHECK-NOT: %ppn = alloca i32**,
+
+// This test case should behave identically to locals_opt_3.rs.
+
+struct float_struct {
+  float f;
+  float f2[2];
+} compound_float;
+
+
+static
+int main(int argc, char* argv[])
+{
+  float f = 0.f;
+  float *pf = &f;
+
+  double d[2][2] = {{0, 1}, {2, 3.0}};
+  struct float_struct s;
+
+  unsigned short us = -1;
+  const unsigned long l = (unsigned long) -1.0e8f;
+
+  {
+    int** ppn = 0;
+    if (ppn) {
+      return -1;
+    }
+  }
+
+  s.f = 10e-4f;
+  s.f2[0] = 1e4f;
+  s.f2[1] = 100.5f;
+
+  double result = pf[0] * d[1][1] * s.f * us * l;
+  return (result == 0 ? 0 : -1);
+}
+
+void the_main() {
+  main(0, 0);
+}
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
diff --git a/lit-tests/rs-filecheck-wrapper.sh b/lit-tests/rs-filecheck-wrapper.sh
new file mode 100755
index 0000000..816c80a
--- /dev/null
+++ b/lit-tests/rs-filecheck-wrapper.sh
@@ -0,0 +1,12 @@
+#!/bin/bash -e
+
+# RS Invocation script to FileCheck
+# Usage: rs-filecheck-wrapper.sh <output-directory> <path-to-FileCheck> <source>
+
+OUTDIR=$1
+FILECHECK=$2
+SOURCEFILE=$3
+
+FILECHECK_INPUTFILE=`basename $SOURCEFILE | sed 's/\.rs\$/.ll/'`
+
+$FILECHECK -input-file $OUTDIR/$FILECHECK_INPUTFILE $SOURCEFILE
diff --git a/llvm-rs-cc.cpp b/llvm-rs-cc.cpp
index d25d6e6..2cd7bb0 100644
--- a/llvm-rs-cc.cpp
+++ b/llvm-rs-cc.cpp
@@ -41,6 +41,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/system_error.h"
+#include "llvm/Target/TargetMachine.h"
 
 #include "slang.h"
 #include "slang_assert.h"
@@ -142,6 +143,12 @@
 
   unsigned int mTargetAPI;
 
+  // Enable emission of debugging symbols
+  unsigned mDebugEmission : 1;
+
+  // The optimization level used in CodeGen, and encoded in emitted bitcode
+  llvm::CodeGenOpt::Level mOptimizationLevel;
+
   RSCCOptions() {
     mOutputType = slang::Slang::OT_Bitcode;
     // Triple/CPU/Features must be hard-coded to our chosen portable ABI.
@@ -154,6 +161,8 @@
     mShowHelp = 0;
     mShowVersion = 0;
     mTargetAPI = RS_VERSION;
+    mDebugEmission = 0;
+    mOptimizationLevel = llvm::CodeGenOpt::Aggressive;
   }
 };
 
@@ -275,6 +284,14 @@
 
     Opts.mShowHelp = Args->hasArg(OPT_help);
     Opts.mShowVersion = Args->hasArg(OPT_version);
+    Opts.mDebugEmission = Args->hasArg(OPT_emit_g);
+
+    size_t OptLevel = Args->getLastArgIntValue(OPT_optimization_level,
+                                               3,
+                                               DiagEngine);
+
+    Opts.mOptimizationLevel = OptLevel == 0 ? llvm::CodeGenOpt::None
+                                            : llvm::CodeGenOpt::Aggressive;
 
     Opts.mTargetAPI = Args->getLastArgIntValue(OPT_target_api,
                                                RS_VERSION,
@@ -458,6 +475,8 @@
                                          Opts.mAllowRSPrefix,
                                          Opts.mOutputDep,
                                          Opts.mTargetAPI,
+                                         Opts.mDebugEmission,
+                                         Opts.mOptimizationLevel,
                                          Opts.mJavaReflectionPathBase,
                                          Opts.mJavaReflectionPackageName);
 
diff --git a/slang.cpp b/slang.cpp
index ff03836..1c33f4d 100644
--- a/slang.cpp
+++ b/slang.cpp
@@ -157,7 +157,7 @@
     LangOpts.C99 = 1;
     LangOpts.CharIsSigned = 1;  // Signed char is our default.
 
-    CodeGenOpts.OptimizationLevel = 3;  /* -O3 */
+    CodeGenOpts.OptimizationLevel = 3;
 
     GlobalInitialized = true;
   }
@@ -450,6 +450,14 @@
   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
 }
 
+void Slang::setDebugMetadataEmission(bool EmitDebug) {
+  CodeGenOpts.DebugInfo = EmitDebug;
+}
+
+void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
+  CodeGenOpts.OptimizationLevel = OptimizationLevel;
+}
+
 void Slang::reset() {
   llvm::errs() << mDiagClient->str();
   mDiagEngine->Reset();
diff --git a/slang.h b/slang.h
index 6b162f4..16f851e 100644
--- a/slang.h
+++ b/slang.h
@@ -28,6 +28,8 @@
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/ADT/StringRef.h"
 
+#include "llvm/Target/TargetMachine.h"
+
 #include "slang_diagnostic_buffer.h"
 #include "slang_pragma_recorder.h"
 
@@ -216,6 +218,10 @@
 
   char const *getErrorMessage() { return mDiagClient->str().c_str(); }
 
+  void setDebugMetadataEmission(bool EmitDebug);
+
+  void setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel);
+
   // Reset the slang compiler state such that it can be reused to compile
   // another file
   virtual void reset();
diff --git a/slang_backend.cpp b/slang_backend.cpp
index 72cd47c..9281f39 100644
--- a/slang_backend.cpp
+++ b/slang_backend.cpp
@@ -208,7 +208,6 @@
                  llvm::raw_ostream *OS,
                  Slang::OutputType OT)
     : ASTConsumer(),
-      mCodeGenOpts(CodeGenOpts),
       mTargetOpts(TargetOpts),
       mpModule(NULL),
       mpOS(OS),
@@ -219,6 +218,7 @@
       mCodeGenPasses(NULL),
       mLLVMContext(llvm::getGlobalContext()),
       mDiagEngine(*DiagEngine),
+      mCodeGenOpts(CodeGenOpts),
       mPragmas(Pragmas) {
   FormattedOutStream.setStream(*mpOS,
                                llvm::formatted_raw_ostream::PRESERVE_STREAM);
diff --git a/slang_backend.h b/slang_backend.h
index 71cd1c7..69721fe 100644
--- a/slang_backend.h
+++ b/slang_backend.h
@@ -48,7 +48,6 @@
 
 class Backend : public clang::ASTConsumer {
  private:
-  const clang::CodeGenOptions &mCodeGenOpts;
   const clang::TargetOptions &mTargetOpts;
 
   llvm::Module *mpModule;
@@ -80,6 +79,7 @@
  protected:
   llvm::LLVMContext &mLLVMContext;
   clang::DiagnosticsEngine &mDiagEngine;
+  const clang::CodeGenOptions &mCodeGenOpts;
 
   PragmaList *mPragmas;
 
diff --git a/slang_rs.cpp b/slang_rs.cpp
index 22e694a..13413c0 100644
--- a/slang_rs.cpp
+++ b/slang_rs.cpp
@@ -261,7 +261,8 @@
     const std::vector<std::string> &AdditionalDepTargets,
     Slang::OutputType OutputType, BitCodeStorageType BitcodeStorage,
     bool AllowRSPrefix, bool OutputDep,
-    unsigned int TargetAPI,
+    unsigned int TargetAPI, bool EmitDebug,
+    llvm::CodeGenOpt::Level OptimizationLevel,
     const std::string &JavaReflectionPathBase,
     const std::string &JavaReflectionPackageName) {
   if (IOFiles.empty())
@@ -284,6 +285,10 @@
     setAdditionalDepTargets(AdditionalDepTargets);
   }
 
+  setDebugMetadataEmission(EmitDebug);
+
+  setOptimizationLevel(OptimizationLevel);
+
   mAllowRSPrefix = AllowRSPrefix;
 
   mTargetAPI = TargetAPI;
diff --git a/slang_rs.h b/slang_rs.h
index 09ec651..b2ef41d 100644
--- a/slang_rs.h
+++ b/slang_rs.h
@@ -120,6 +120,11 @@
   //
   // @JavaReflectionPathBase - The path base for storing reflection files.
   //
+  // @EmitDebug - true to allow debug metadata emission
+  //
+  // @OptimizationLevel - code generation optimization level: None is recommended for
+  //                      interactive debugging. The default is Aggresive.
+  //
   // @JavaReflectionPackageName - The package name given by user in command
   //                              line. This may override the package name
   //                              specified in the .rs using #pragma.
@@ -130,7 +135,8 @@
                const std::vector<std::string> &AdditionalDepTargets,
                Slang::OutputType OutputType, BitCodeStorageType BitcodeStorage,
                bool AllowRSPrefix, bool OutputDep,
-               unsigned int TargetAPI,
+               unsigned int TargetAPI, bool EmitDebug,
+               llvm::CodeGenOpt::Level OptimizationLevel,
                const std::string &JavaReflectionPathBase,
                const std::string &JavaReflectionPackageName);
 
diff --git a/slang_rs_backend.cpp b/slang_rs_backend.cpp
index 20b9395..ba9c26b 100644
--- a/slang_rs_backend.cpp
+++ b/slang_rs_backend.cpp
@@ -19,6 +19,8 @@
 #include <string>
 #include <vector>
 
+#include "clang/Frontend/CodeGenOptions.h"
+
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/StringExtras.h"
 
@@ -29,6 +31,7 @@
 #include "llvm/Metadata.h"
 #include "llvm/Module.h"
 
+#include "llvm/Support/DebugLoc.h"
 #include "llvm/Support/IRBuilder.h"
 
 #include "slang_assert.h"
@@ -61,6 +64,7 @@
     mExportForEachSignatureMetadata(NULL),
     mExportTypeMetadata(NULL),
     mRSObjectSlotsMetadata(NULL),
+    mRSOptimizationMetadata(NULL),
     mRefCount(mContext->getASTContext()) {
 }
 
@@ -194,6 +198,16 @@
     return;
   }
 
+  // Write optimization level
+  llvm::SmallVector<llvm::Value*, 1> OptimizationOption;
+  OptimizationOption.push_back(llvm::ConstantInt::get(
+    mLLVMContext, llvm::APInt(32, mCodeGenOpts.OptimizationLevel)));
+
+  if (mRSOptimizationMetadata == NULL)
+    mRSOptimizationMetadata = M->getOrInsertNamedMetadata(OPTIMIZATION_LEVEL_MN);
+  mRSOptimizationMetadata->addOperand(
+    llvm::MDNode::get(mLLVMContext, OptimizationOption));
+
   // Dump export variable info
   if (mContext->hasExportVar()) {
     int slotCount = 0;
diff --git a/slang_rs_backend.h b/slang_rs_backend.h
index 0dc70e6..58e4535 100644
--- a/slang_rs_backend.h
+++ b/slang_rs_backend.h
@@ -54,6 +54,7 @@
   llvm::NamedMDNode *mExportTypeMetadata;
   llvm::NamedMDNode *mExportElementMetadata;
   llvm::NamedMDNode *mRSObjectSlotsMetadata;
+  llvm::NamedMDNode *mRSOptimizationMetadata;
 
   RSObjectRefCount mRefCount;
 
diff --git a/slang_rs_metadata.h b/slang_rs_metadata.h
index 63e7e0f..4d2bd0d 100644
--- a/slang_rs_metadata.h
+++ b/slang_rs_metadata.h
@@ -33,4 +33,6 @@
 
 #define RS_EXPORT_FOREACH_MN "#rs_export_foreach"
 
+#define OPTIMIZATION_LEVEL_MN "#optimization_level"
+
 #endif  // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_METADATA_H_  NOLINT