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/TextDiagnosticPrinter.h"
     21 #include "clang/Frontend/Utils.h"
     22 
     23 #include "llvm/ADT/SmallVector.h"
     24 #include "llvm/ADT/IntrusiveRefCntPtr.h"
     25 
     26 #include "llvm/Option/OptTable.h"
     27 #include "llvm/Support/CommandLine.h"
     28 #include "llvm/Support/ManagedStatic.h"
     29 #include "llvm/Support/MemoryBuffer.h"
     30 #include "llvm/Support/Path.h"
     31 #include "llvm/Support/raw_ostream.h"
     32 #include "llvm/Target/TargetMachine.h"
     33 
     34 #include "rs_cc_options.h"
     35 #include "slang.h"
     36 #include "slang_assert.h"
     37 #include "slang_diagnostic_buffer.h"
     38 #include "slang_rs.h"
     39 #include "slang_rs_reflect_utils.h"
     40 
     41 #include <list>
     42 #include <set>
     43 #include <string>
     44 
     45 // SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from
     46 // $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in
     47 // main().
     48 static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
     49                                           llvm::StringRef S) {
     50   return SavedStrings.insert(S).first->c_str();
     51 }
     52 static void ExpandArgsFromBuf(const char *Arg,
     53                               llvm::SmallVectorImpl<const char*> &ArgVector,
     54                               std::set<std::string> &SavedStrings);
     55 static void ExpandArgv(int argc, const char **argv,
     56                        llvm::SmallVectorImpl<const char*> &ArgVector,
     57                        std::set<std::string> &SavedStrings);
     58 
     59 static const char *DetermineOutputFile(const std::string &OutputDir,
     60                                        const std::string &PathSuffix,
     61                                        const char *InputFile,
     62                                        slang::Slang::OutputType OutputType,
     63                                        std::set<std::string> &SavedStrings) {
     64   if (OutputType == slang::Slang::OT_Nothing)
     65     return "/dev/null";
     66 
     67   std::string OutputFile(OutputDir);
     68 
     69   // Append '/' to Opts.mBitcodeOutputDir if not presents
     70   if (!OutputFile.empty() &&
     71       (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
     72     OutputFile.append(1, OS_PATH_SEPARATOR);
     73 
     74   if (!PathSuffix.empty()) {
     75     OutputFile.append(PathSuffix);
     76     OutputFile.append(1, OS_PATH_SEPARATOR);
     77   }
     78 
     79   if (OutputType == slang::Slang::OT_Dependency) {
     80     // The build system wants the .d file name stem to be exactly the same as
     81     // the source .rs file, instead of the .bc file.
     82     OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
     83   } else {
     84     OutputFile.append(
     85         slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
     86   }
     87 
     88   switch (OutputType) {
     89     case slang::Slang::OT_Dependency: {
     90       OutputFile.append(".d");
     91       break;
     92     }
     93     case slang::Slang::OT_Assembly: {
     94       OutputFile.append(".S");
     95       break;
     96     }
     97     case slang::Slang::OT_LLVMAssembly: {
     98       OutputFile.append(".ll");
     99       break;
    100     }
    101     case slang::Slang::OT_Object: {
    102       OutputFile.append(".o");
    103       break;
    104     }
    105     case slang::Slang::OT_Bitcode: {
    106       OutputFile.append(".bc");
    107       break;
    108     }
    109     case slang::Slang::OT_Nothing:
    110     default: {
    111       slangAssert(false && "Invalid output type!");
    112     }
    113   }
    114 
    115   return SaveStringInSet(SavedStrings, OutputFile);
    116 }
    117 
    118 typedef std::list<std::pair<const char*, const char*> > NamePairList;
    119 
    120 /*
    121  * Compile the Inputs.
    122  *
    123  * Returns 0 on success and nonzero on failure.
    124  *
    125  * IOFiles - list of (foo.rs, foo.bc) pairs of input/output files.
    126  * IOFiles32 - list of input/output pairs for 32-bit compilation.
    127  * Inputs - input filenames.
    128  * Opts - options controlling compilation.
    129  * DiagEngine - Clang diagnostic engine (for creating diagnostics).
    130  * DiagClient - Slang diagnostic consumer (collects and displays diagnostics).
    131  * SavedStrings - expanded strings copied from argv source input files.
    132  *
    133  * We populate IOFiles dynamically while working through the list of Inputs.
    134  * On any 64-bit compilation, we pass back in the 32-bit pairs of files as
    135  * IOFiles32. This allows the 64-bit compiler to later bundle up both the
    136  * 32-bit and 64-bit bitcode outputs to be included in the final reflected
    137  * source code that is emitted.
    138  */
    139 static int compileFiles(NamePairList *IOFiles, NamePairList *IOFiles32,
    140     const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts,
    141     clang::DiagnosticsEngine *DiagEngine, slang::DiagnosticBuffer *DiagClient,
    142     std::set<std::string> *SavedStrings) {
    143   NamePairList DepFiles;
    144   std::string PathSuffix = "";
    145   bool CompileSecondTimeFor64Bit = false;
    146 
    147   // In our mixed 32/64-bit path, we need to suffix our files differently for
    148   // both 32-bit and 64-bit versions.
    149   if (Opts.mEmit3264) {
    150     if (Opts.mBitWidth == 64) {
    151       PathSuffix = "bc64";
    152       CompileSecondTimeFor64Bit = true;
    153     } else {
    154       PathSuffix = "bc32";
    155     }
    156   }
    157 
    158   for (int i = 0, e = Inputs.size(); i != e; i++) {
    159     const char *InputFile = Inputs[i];
    160 
    161     const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir,
    162                                                    PathSuffix, InputFile,
    163                                                    slang::Slang::OT_Bitcode,
    164                                                    *SavedStrings);
    165     const char *OutputFile = BCOutputFile;
    166 
    167     if (Opts.mEmitDependency) {
    168       // The dependency file is always emitted without a PathSuffix.
    169       // Collisions between 32-bit and 64-bit files don't make a difference,
    170       // because they share the same sources/dependencies.
    171       const char *DepOutputFile =
    172           DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile,
    173                               slang::Slang::OT_Dependency, *SavedStrings);
    174       if (Opts.mOutputType == slang::Slang::OT_Dependency) {
    175         OutputFile = DepOutputFile;
    176       }
    177 
    178       DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
    179     }
    180 
    181     IOFiles->push_back(std::make_pair(InputFile, OutputFile));
    182   }
    183 
    184   std::unique_ptr<slang::SlangRS> Compiler(new slang::SlangRS());
    185   Compiler->init(Opts.mBitWidth, DiagEngine, DiagClient);
    186   int CompileFailed = !Compiler->compile(*IOFiles, *IOFiles32, DepFiles, Opts);
    187   // We suppress warnings (via reset) if we are doing a second compilation.
    188   Compiler->reset(CompileSecondTimeFor64Bit);
    189   return CompileFailed;
    190 }
    191 
    192 #define str(s) #s
    193 #define wrap_str(s) str(s)
    194 static void llvm_rs_cc_VersionPrinter() {
    195   llvm::raw_ostream &OS = llvm::outs();
    196   OS << "llvm-rs-cc: Renderscript compiler\n"
    197      << "  (http://developer.android.com/guide/topics/renderscript)\n"
    198      << "  based on LLVM (http://llvm.org):\n";
    199   OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
    200   OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
    201      << SLANG_MAXIMUM_TARGET_API;
    202   OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
    203 #ifndef __DISABLE_ASSERTS
    204   OS << " with assertions";
    205 #endif
    206   OS << ".\n";
    207 }
    208 #undef wrap_str
    209 #undef str
    210 
    211 int main(int argc, const char **argv) {
    212   std::set<std::string> SavedStrings;
    213   llvm::SmallVector<const char*, 256> ArgVector;
    214   slang::RSCCOptions Opts;
    215   llvm::SmallVector<const char*, 16> Inputs;
    216   std::string Argv0;
    217 
    218   llvm::llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
    219 
    220   ExpandArgv(argc, argv, ArgVector, SavedStrings);
    221 
    222   // Argv0
    223   Argv0 = llvm::sys::path::stem(ArgVector[0]);
    224 
    225   // Setup diagnostic engine
    226   slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer();
    227 
    228   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
    229     new clang::DiagnosticIDs());
    230 
    231   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
    232     new clang::DiagnosticOptions());
    233   clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, DiagClient, true);
    234 
    235   slang::Slang::GlobalInitialization();
    236 
    237   slang::ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
    238 
    239   // Exits when there's any error occurred during parsing the arguments
    240   if (DiagEngine.hasErrorOccurred()) {
    241     llvm::errs() << DiagClient->str();
    242     return 1;
    243   }
    244 
    245   if (Opts.mShowHelp) {
    246     std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable());
    247     OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
    248                       "Renderscript source compiler");
    249     return 0;
    250   }
    251 
    252   if (Opts.mShowVersion) {
    253     llvm_rs_cc_VersionPrinter();
    254     return 0;
    255   }
    256 
    257   // No input file
    258   if (Inputs.empty()) {
    259     DiagEngine.Report(clang::diag::err_drv_no_input_files);
    260     llvm::errs() << DiagClient->str();
    261     return 1;
    262   }
    263 
    264   // Prepare input data for RS compiler.
    265   NamePairList IOFiles64;
    266   NamePairList IOFiles32;
    267 
    268   int CompileFailed = compileFiles(&IOFiles32, &IOFiles32, Inputs, Opts,
    269                                    &DiagEngine, DiagClient, &SavedStrings);
    270 
    271   // Handle the 64-bit case too!
    272   if (Opts.mEmit3264 && !CompileFailed) {
    273     Opts.mBitWidth = 64;
    274     CompileFailed = compileFiles(&IOFiles64, &IOFiles32, Inputs, Opts,
    275                                  &DiagEngine, DiagClient, &SavedStrings);
    276   }
    277 
    278   return CompileFailed;
    279 }
    280 
    281 ///////////////////////////////////////////////////////////////////////////////
    282 
    283 // ExpandArgsFromBuf -
    284 static void ExpandArgsFromBuf(const char *Arg,
    285                               llvm::SmallVectorImpl<const char*> &ArgVector,
    286                               std::set<std::string> &SavedStrings) {
    287   const char *FName = Arg + 1;
    288   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MBOrErr =
    289       llvm::MemoryBuffer::getFile(FName);
    290   if (MBOrErr.getError()) {
    291     // Unable to open the file
    292     ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
    293     return;
    294   }
    295   std::unique_ptr<llvm::MemoryBuffer> MemBuf = std::move(MBOrErr.get());
    296 
    297   const char *Buf = MemBuf->getBufferStart();
    298   char InQuote = ' ';
    299   std::string CurArg;
    300 
    301   for (const char *P = Buf; ; ++P) {
    302     if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
    303       if (!CurArg.empty()) {
    304         if (CurArg[0] != '@') {
    305           ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
    306         } else {
    307           ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
    308         }
    309 
    310         CurArg = "";
    311       }
    312       if (*P == '\0')
    313         break;
    314       else
    315         continue;
    316     }
    317 
    318     if (isspace(*P)) {
    319       if (InQuote != ' ')
    320         CurArg.push_back(*P);
    321       continue;
    322     }
    323 
    324     if (*P == '"' || *P == '\'') {
    325       if (InQuote == *P)
    326         InQuote = ' ';
    327       else if (InQuote == ' ')
    328         InQuote = *P;
    329       else
    330         CurArg.push_back(*P);
    331       continue;
    332     }
    333 
    334     if (*P == '\\') {
    335       ++P;
    336       if (*P != '\0')
    337         CurArg.push_back(*P);
    338       continue;
    339     }
    340     CurArg.push_back(*P);
    341   }
    342 }
    343 
    344 // ExpandArgsFromBuf -
    345 static void ExpandArgv(int argc, const char **argv,
    346                        llvm::SmallVectorImpl<const char*> &ArgVector,
    347                        std::set<std::string> &SavedStrings) {
    348   for (int i = 0; i < argc; ++i) {
    349     const char *Arg = argv[i];
    350     if (Arg[0] != '@') {
    351       ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
    352       continue;
    353     }
    354 
    355     ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
    356   }
    357 }
    358