Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2010-2012, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "clang/Basic/DiagnosticOptions.h"
     18 #include "clang/Driver/DriverDiagnostic.h"
     19 #include "clang/Driver/Options.h"
     20 #include "clang/Frontend/CompilerInvocation.h"
     21 #include "clang/Frontend/FrontendDiagnostic.h"
     22 #include "clang/Frontend/TextDiagnosticPrinter.h"
     23 #include "clang/Frontend/Utils.h"
     24 
     25 #include "llvm/ADT/SmallVector.h"
     26 #include "llvm/ADT/IntrusiveRefCntPtr.h"
     27 
     28 #include "llvm/Option/OptTable.h"
     29 #include "llvm/Support/Allocator.h"
     30 #include "llvm/Support/ManagedStatic.h"
     31 #include "llvm/Support/MemoryBuffer.h"
     32 #include "llvm/Support/Path.h"
     33 #include "llvm/Support/raw_ostream.h"
     34 #include "llvm/Support/Signals.h"
     35 #include "llvm/Support/StringSaver.h"
     36 #include "llvm/Support/TargetSelect.h"
     37 #include "llvm/Target/TargetMachine.h"
     38 
     39 #include "os_sep.h"
     40 #include "rs_cc_options.h"
     41 #include "slang.h"
     42 #include "slang_assert.h"
     43 #include "slang_diagnostic_buffer.h"
     44 #include "slang_rs_reflect_utils.h"
     45 #include "slang_rs_reflection_state.h"
     46 
     47 #include <list>
     48 #include <set>
     49 #include <string>
     50 
     51 namespace {
     52 class StringSet {
     53 public:
     54   const char *save(const char *Str) {
     55     return Strings.save(Str);
     56   }
     57 
     58   StringSet() : Strings(A), A() {}
     59 
     60   llvm::StringSaver & getStringSaver() { return Strings; }
     61 
     62 private:
     63   llvm::StringSaver Strings;
     64   llvm::BumpPtrAllocator A;
     65 };
     66 }
     67 
     68 static const char *DetermineOutputFile(const std::string &OutputDir,
     69                                        const std::string &PathSuffix,
     70                                        const char *InputFile,
     71                                        slang::Slang::OutputType OutputType,
     72                                        StringSet *SavedStrings) {
     73   if (OutputType == slang::Slang::OT_Nothing)
     74     return "/dev/null";
     75 
     76   std::string OutputFile(OutputDir);
     77 
     78   // Append '/' to Opts.mBitcodeOutputDir if not presents
     79   if (!OutputFile.empty() &&
     80       (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
     81     OutputFile.append(1, OS_PATH_SEPARATOR);
     82 
     83   if (!PathSuffix.empty()) {
     84     OutputFile.append(PathSuffix);
     85     OutputFile.append(1, OS_PATH_SEPARATOR);
     86   }
     87 
     88   if (OutputType == slang::Slang::OT_Dependency) {
     89     // The build system wants the .d file name stem to be exactly the same as
     90     // the source .rs file, instead of the .bc file.
     91     OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
     92   } else {
     93     OutputFile.append(
     94         slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
     95   }
     96 
     97   switch (OutputType) {
     98     case slang::Slang::OT_Dependency: {
     99       OutputFile.append(".d");
    100       break;
    101     }
    102     case slang::Slang::OT_Assembly: {
    103       OutputFile.append(".S");
    104       break;
    105     }
    106     case slang::Slang::OT_LLVMAssembly: {
    107       OutputFile.append(".ll");
    108       break;
    109     }
    110     case slang::Slang::OT_Object: {
    111       OutputFile.append(".o");
    112       break;
    113     }
    114     case slang::Slang::OT_Bitcode: {
    115       OutputFile.append(".bc");
    116       break;
    117     }
    118     case slang::Slang::OT_Nothing:
    119     default: {
    120       slangAssert(false && "Invalid output type!");
    121     }
    122   }
    123 
    124   return SavedStrings->save(OutputFile.c_str());
    125 }
    126 
    127 typedef std::list<std::pair<const char*, const char*> > NamePairList;
    128 
    129 /*
    130  * Compile the Inputs.
    131  *
    132  * Returns 0 on success and nonzero on failure.
    133  *
    134  * IOFiles - list of (foo.rs, foo.bc) pairs of input/output files.
    135  * IOFiles32 - list of input/output pairs for 32-bit compilation.
    136  * Inputs - input filenames.
    137  * Opts - options controlling compilation.
    138  * DiagEngine - Clang diagnostic engine (for creating diagnostics).
    139  * DiagClient - Slang diagnostic consumer (collects and displays diagnostics).
    140  * SavedStrings - expanded strings copied from argv source input files.
    141  *
    142  * We populate IOFiles dynamically while working through the list of Inputs.
    143  * On any 64-bit compilation, we pass back in the 32-bit pairs of files as
    144  * IOFiles32. This allows the 64-bit compiler to later bundle up both the
    145  * 32-bit and 64-bit bitcode outputs to be included in the final reflected
    146  * source code that is emitted.
    147  */
    148 static void makeFileList(NamePairList *IOFiles, NamePairList *DepFiles,
    149     const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts,
    150     StringSet *SavedStrings) {
    151   std::string PathSuffix = "";
    152   // In our mixed 32/64-bit path, we need to suffix our files differently for
    153   // both 32-bit and 64-bit versions.
    154   if (Opts.mEmit3264) {
    155     if (Opts.mBitWidth == 64) {
    156       PathSuffix = "bc64";
    157     } else {
    158       PathSuffix = "bc32";
    159     }
    160   }
    161 
    162   for (int i = 0, e = Inputs.size(); i != e; i++) {
    163     const char *InputFile = Inputs[i];
    164 
    165     const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir,
    166                                                    PathSuffix, InputFile,
    167                                                    Opts.mOutputType,
    168                                                    SavedStrings);
    169     const char *OutputFile = BCOutputFile;
    170 
    171     if (Opts.mEmitDependency) {
    172       // The dependency file is always emitted without a PathSuffix.
    173       // Collisions between 32-bit and 64-bit files don't make a difference,
    174       // because they share the same sources/dependencies.
    175       const char *DepOutputFile =
    176           DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile,
    177                               slang::Slang::OT_Dependency, SavedStrings);
    178       if (Opts.mOutputType == slang::Slang::OT_Dependency) {
    179         OutputFile = DepOutputFile;
    180       }
    181 
    182       DepFiles->push_back(std::make_pair(BCOutputFile, DepOutputFile));
    183     }
    184 
    185     IOFiles->push_back(std::make_pair(InputFile, OutputFile));
    186   }
    187 }
    188 
    189 #define str(s) #s
    190 #define wrap_str(s) str(s)
    191 static void llvm_rs_cc_VersionPrinter() {
    192   llvm::raw_ostream &OS = llvm::outs();
    193   OS << "llvm-rs-cc: Renderscript compiler\n"
    194      << "  (http://developer.android.com/guide/topics/renderscript)\n"
    195      << "  based on LLVM (http://llvm.org):\n";
    196   OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
    197      << SLANG_MAXIMUM_TARGET_API;
    198   OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
    199 #ifndef __DISABLE_ASSERTS
    200   OS << " with assertions";
    201 #endif
    202   OS << ".\n";
    203 }
    204 #undef wrap_str
    205 #undef str
    206 
    207 static void LLVMErrorHandler(void *UserData, const std::string &Message,
    208                              bool GenCrashDialog) {
    209   clang::DiagnosticsEngine *DiagEngine =
    210       static_cast<clang::DiagnosticsEngine *>(UserData);
    211 
    212   DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
    213 
    214   // Run the interrupt handlers to make sure any special cleanups get done, in
    215   // particular that we remove files registered with RemoveFileOnSignal.
    216   llvm::sys::RunInterruptHandlers();
    217 
    218   exit(1);
    219 }
    220 
    221 int main(int argc, const char **argv) {
    222   llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
    223   LLVMInitializeARMTargetInfo();
    224   LLVMInitializeARMTarget();
    225   LLVMInitializeARMAsmPrinter();
    226 
    227   StringSet SavedStrings; // Keeps track of strings to be destroyed at the end.
    228 
    229   // Parse the command line arguments and respond to show help & version
    230   // commands.
    231   llvm::SmallVector<const char *, 16> Inputs;
    232   slang::RSCCOptions Opts;
    233   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts =
    234       new clang::DiagnosticOptions();
    235   if (!slang::ParseArguments(llvm::makeArrayRef(argv, argc), Inputs, Opts,
    236                              *DiagOpts, SavedStrings.getStringSaver())) {
    237     // Exits when there's any error occurred during parsing the arguments
    238     return 1;
    239   }
    240   if (Opts.mShowHelp) {
    241     std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable());
    242     const std::string Argv0 = llvm::sys::path::stem(argv[0]);
    243     OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
    244                       "Renderscript source compiler");
    245     return 0;
    246   }
    247   if (Opts.mShowVersion) {
    248     llvm_rs_cc_VersionPrinter();
    249     return 0;
    250   }
    251 
    252   // Initialize the diagnostic objects
    253   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
    254       new clang::DiagnosticIDs());
    255   slang::DiagnosticBuffer DiagsBuffer;
    256   clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, &DiagsBuffer, false);
    257   clang::ProcessWarningOptions(DiagEngine, *DiagOpts);
    258   (void)DiagEngine.setSeverityForGroup(clang::diag::Flavor::WarningOrError,
    259                                        "implicit-function-declaration",
    260                                        clang::diag::Severity::Error);
    261 
    262   // Report error if no input file
    263   if (Inputs.empty()) {
    264     DiagEngine.Report(clang::diag::err_drv_no_input_files);
    265     llvm::errs() << DiagsBuffer.str();
    266     return 1;
    267   }
    268 
    269   llvm::install_fatal_error_handler(LLVMErrorHandler, &DiagEngine);
    270 
    271   slang::ReflectionState Reflection;
    272 
    273   // Compile the 32 bit version
    274   NamePairList IOFiles32;
    275   NamePairList DepFiles32;
    276   makeFileList(&IOFiles32, &DepFiles32, Inputs, Opts, &SavedStrings);
    277 
    278   int CompileFailed = 0;
    279   // Handle 32-bit case for Java and C++ reflection.
    280   // For Java, both 32bit and 64bit will be generated.
    281   // For C++, either 64bit or 32bit will be generated based on the target.
    282   if (Opts.mEmit3264 || Opts.mBitWidth == 32) {
    283       std::unique_ptr<slang::Slang> Compiler(
    284           new slang::Slang(32, &DiagEngine, &DiagsBuffer));
    285       CompileFailed =
    286           !Compiler->compile(IOFiles32, IOFiles32, DepFiles32, Opts, *DiagOpts, &Reflection);
    287   }
    288 
    289   // Handle the 64-bit case too!
    290   bool needEmit64 = Opts.mEmit3264 || Opts.mBitWidth == 64;
    291   if (needEmit64 && !CompileFailed) {
    292     Opts.mBitWidth = 64;
    293     NamePairList IOFiles64;
    294     NamePairList DepFiles64;
    295     makeFileList(&IOFiles64, &DepFiles64, Inputs, Opts, &SavedStrings);
    296 
    297     std::unique_ptr<slang::Slang> Compiler(
    298         new slang::Slang(64, &DiagEngine, &DiagsBuffer));
    299     CompileFailed =
    300         !Compiler->compile(IOFiles64, IOFiles32, DepFiles64, Opts, *DiagOpts, &Reflection);
    301   }
    302 
    303   llvm::errs() << DiagsBuffer.str();
    304   llvm::remove_fatal_error_handler();
    305   return CompileFailed;
    306 }
    307