Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2010, 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 <cstdlib>
     18 #include <list>
     19 #include <set>
     20 #include <string>
     21 #include <utility>
     22 #include <vector>
     23 
     24 #include "clang/Driver/Arg.h"
     25 #include "clang/Driver/ArgList.h"
     26 #include "clang/Driver/DriverDiagnostic.h"
     27 #include "clang/Driver/Option.h"
     28 #include "clang/Driver/OptTable.h"
     29 
     30 #include "clang/Frontend/DiagnosticOptions.h"
     31 #include "clang/Frontend/TextDiagnosticPrinter.h"
     32 #include "clang/Frontend/Utils.h"
     33 
     34 #include "llvm/ADT/SmallVector.h"
     35 #include "llvm/ADT/IntrusiveRefCntPtr.h"
     36 #include "llvm/ADT/OwningPtr.h"
     37 
     38 #include "llvm/Support/CommandLine.h"
     39 #include "llvm/Support/ManagedStatic.h"
     40 #include "llvm/Support/MemoryBuffer.h"
     41 #include "llvm/Support/Path.h"
     42 #include "llvm/Support/raw_ostream.h"
     43 #include "llvm/Support/system_error.h"
     44 #include "llvm/Target/TargetMachine.h"
     45 
     46 #include "slang.h"
     47 #include "slang_assert.h"
     48 #include "slang_diagnostic_buffer.h"
     49 #include "slang_rs.h"
     50 #include "slang_rs_reflect_utils.h"
     51 
     52 // Class under clang::driver used are enumerated here.
     53 using clang::driver::arg_iterator;
     54 using clang::driver::options::DriverOption;
     55 using clang::driver::Arg;
     56 using clang::driver::ArgList;
     57 using clang::driver::InputArgList;
     58 using clang::driver::Option;
     59 using clang::driver::OptTable;
     60 
     61 // SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from
     62 // $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in
     63 // main().
     64 static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
     65                                           llvm::StringRef S) {
     66   return SavedStrings.insert(S).first->c_str();
     67 }
     68 static void ExpandArgsFromBuf(const char *Arg,
     69                               llvm::SmallVectorImpl<const char*> &ArgVector,
     70                               std::set<std::string> &SavedStrings);
     71 static void ExpandArgv(int argc, const char **argv,
     72                        llvm::SmallVectorImpl<const char*> &ArgVector,
     73                        std::set<std::string> &SavedStrings);
     74 
     75 enum {
     76   OPT_INVALID = 0,  // This is not an option ID.
     77 #define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
     78                HELPTEXT, METAVAR) OPT_##ID,
     79 #include "RSCCOptions.inc"
     80   LastOption
     81 #undef OPTION
     82 };
     83 
     84 static const OptTable::Info RSCCInfoTable[] = {
     85 #define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
     86                HELPTEXT, METAVAR)   \
     87   { NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \
     88     OPT_##GROUP, OPT_##ALIAS },
     89 #include "RSCCOptions.inc"
     90 };
     91 
     92 class RSCCOptTable : public OptTable {
     93  public:
     94   RSCCOptTable()
     95       : OptTable(RSCCInfoTable,
     96                  sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {
     97   }
     98 };
     99 
    100 OptTable *createRSCCOptTable() {
    101   return new RSCCOptTable();
    102 }
    103 
    104 ///////////////////////////////////////////////////////////////////////////////
    105 
    106 class RSCCOptions {
    107  public:
    108   // The include search paths
    109   std::vector<std::string> mIncludePaths;
    110 
    111   // The output directory, if any.
    112   std::string mOutputDir;
    113 
    114   // The output type
    115   slang::Slang::OutputType mOutputType;
    116 
    117   unsigned mAllowRSPrefix : 1;
    118 
    119   // The name of the target triple to compile for.
    120   std::string mTriple;
    121 
    122   // The name of the target CPU to generate code for.
    123   std::string mCPU;
    124 
    125   // The list of target specific features to enable or disable -- this should
    126   // be a list of strings starting with by '+' or '-'.
    127   std::vector<std::string> mFeatures;
    128 
    129   std::string mJavaReflectionPathBase;
    130 
    131   std::string mJavaReflectionPackageName;
    132 
    133   slang::BitCodeStorageType mBitcodeStorage;
    134 
    135   unsigned mOutputDep : 1;
    136 
    137   std::string mOutputDepDir;
    138 
    139   std::vector<std::string> mAdditionalDepTargets;
    140 
    141   unsigned mShowHelp : 1;  // Show the -help text.
    142   unsigned mShowVersion : 1;  // Show the -version text.
    143 
    144   unsigned int mTargetAPI;
    145 
    146   // Enable emission of debugging symbols
    147   unsigned mDebugEmission : 1;
    148 
    149   // The optimization level used in CodeGen, and encoded in emitted bitcode
    150   llvm::CodeGenOpt::Level mOptimizationLevel;
    151 
    152   RSCCOptions() {
    153     mOutputType = slang::Slang::OT_Bitcode;
    154     // Triple/CPU/Features must be hard-coded to our chosen portable ABI.
    155     mTriple = "armv7-none-linux-gnueabi";
    156     mCPU = "";
    157     slangAssert(mFeatures.empty());
    158     mFeatures.push_back("+long64");
    159     mBitcodeStorage = slang::BCST_APK_RESOURCE;
    160     mOutputDep = 0;
    161     mShowHelp = 0;
    162     mShowVersion = 0;
    163     mTargetAPI = RS_VERSION;
    164     mDebugEmission = 0;
    165     mOptimizationLevel = llvm::CodeGenOpt::Aggressive;
    166   }
    167 };
    168 
    169 // ParseArguments -
    170 static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector,
    171                            llvm::SmallVectorImpl<const char*> &Inputs,
    172                            RSCCOptions &Opts,
    173                            clang::DiagnosticsEngine &DiagEngine) {
    174   if (ArgVector.size() > 1) {
    175     const char **ArgBegin = ArgVector.data() + 1;
    176     const char **ArgEnd = ArgVector.data() + ArgVector.size();
    177     unsigned MissingArgIndex, MissingArgCount;
    178     llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable());
    179     llvm::OwningPtr<InputArgList> Args(
    180       OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount));
    181 
    182     // Check for missing argument error.
    183     if (MissingArgCount)
    184       DiagEngine.Report(clang::diag::err_drv_missing_argument)
    185         << Args->getArgString(MissingArgIndex) << MissingArgCount;
    186 
    187     clang::DiagnosticOptions DiagOpts;
    188     DiagOpts.IgnoreWarnings = Args->hasArg(OPT_w);
    189     DiagOpts.Warnings = Args->getAllArgValues(OPT_W);
    190     clang::ProcessWarningOptions(DiagEngine, DiagOpts);
    191 
    192     // Issue errors on unknown arguments.
    193     for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN),
    194         ie = Args->filtered_end(); it != ie; ++it)
    195       DiagEngine.Report(clang::diag::err_drv_unknown_argument)
    196         << (*it)->getAsString(*Args);
    197 
    198     for (ArgList::const_iterator it = Args->begin(), ie = Args->end();
    199         it != ie; ++it) {
    200       const Arg *A = *it;
    201       if (A->getOption().getKind() == Option::InputClass)
    202         Inputs.push_back(A->getValue(*Args));
    203     }
    204 
    205     Opts.mIncludePaths = Args->getAllArgValues(OPT_I);
    206 
    207     Opts.mOutputDir = Args->getLastArgValue(OPT_o);
    208 
    209     if (const Arg *A = Args->getLastArg(OPT_M_Group)) {
    210       switch (A->getOption().getID()) {
    211         case OPT_M: {
    212           Opts.mOutputDep = 1;
    213           Opts.mOutputType = slang::Slang::OT_Dependency;
    214           break;
    215         }
    216         case OPT_MD: {
    217           Opts.mOutputDep = 1;
    218           Opts.mOutputType = slang::Slang::OT_Bitcode;
    219           break;
    220         }
    221         default: {
    222           slangAssert(false && "Invalid option in M group!");
    223         }
    224       }
    225     }
    226 
    227     if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) {
    228       switch (A->getOption().getID()) {
    229         case OPT_emit_asm: {
    230           Opts.mOutputType = slang::Slang::OT_Assembly;
    231           break;
    232         }
    233         case OPT_emit_llvm: {
    234           Opts.mOutputType = slang::Slang::OT_LLVMAssembly;
    235           break;
    236         }
    237         case OPT_emit_bc: {
    238           Opts.mOutputType = slang::Slang::OT_Bitcode;
    239           break;
    240         }
    241         case OPT_emit_nothing: {
    242           Opts.mOutputType = slang::Slang::OT_Nothing;
    243           break;
    244         }
    245         default: {
    246           slangAssert(false && "Invalid option in output type group!");
    247         }
    248       }
    249     }
    250 
    251     if (Opts.mOutputDep &&
    252         ((Opts.mOutputType != slang::Slang::OT_Bitcode) &&
    253          (Opts.mOutputType != slang::Slang::OT_Dependency)))
    254       DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
    255           << Args->getLastArg(OPT_M_Group)->getAsString(*Args)
    256           << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args);
    257 
    258     Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix);
    259 
    260     Opts.mJavaReflectionPathBase =
    261         Args->getLastArgValue(OPT_java_reflection_path_base);
    262     Opts.mJavaReflectionPackageName =
    263         Args->getLastArgValue(OPT_java_reflection_package_name);
    264 
    265     llvm::StringRef BitcodeStorageValue =
    266         Args->getLastArgValue(OPT_bitcode_storage);
    267     if (BitcodeStorageValue == "ar")
    268       Opts.mBitcodeStorage = slang::BCST_APK_RESOURCE;
    269     else if (BitcodeStorageValue == "jc")
    270       Opts.mBitcodeStorage = slang::BCST_JAVA_CODE;
    271     else if (!BitcodeStorageValue.empty())
    272       DiagEngine.Report(clang::diag::err_drv_invalid_value)
    273           << OptParser->getOptionName(OPT_bitcode_storage)
    274           << BitcodeStorageValue;
    275 
    276     if (Args->hasArg(OPT_reflect_cpp)) {
    277       Opts.mBitcodeStorage = slang::BCST_CPP_CODE;
    278     }
    279 
    280     Opts.mOutputDepDir =
    281         Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir);
    282     Opts.mAdditionalDepTargets =
    283         Args->getAllArgValues(OPT_additional_dep_target);
    284 
    285     Opts.mShowHelp = Args->hasArg(OPT_help);
    286     Opts.mShowVersion = Args->hasArg(OPT_version);
    287     Opts.mDebugEmission = Args->hasArg(OPT_emit_g);
    288 
    289     size_t OptLevel = Args->getLastArgIntValue(OPT_optimization_level,
    290                                                3,
    291                                                DiagEngine);
    292 
    293     Opts.mOptimizationLevel = OptLevel == 0 ? llvm::CodeGenOpt::None
    294                                             : llvm::CodeGenOpt::Aggressive;
    295 
    296     Opts.mTargetAPI = Args->getLastArgIntValue(OPT_target_api,
    297                                                RS_VERSION,
    298                                                DiagEngine);
    299   }
    300 
    301   return;
    302 }
    303 
    304 static const char *DetermineOutputFile(const std::string &OutputDir,
    305                                        const char *InputFile,
    306                                        slang::Slang::OutputType OutputType,
    307                                        std::set<std::string> &SavedStrings) {
    308   if (OutputType == slang::Slang::OT_Nothing)
    309     return "/dev/null";
    310 
    311   std::string OutputFile(OutputDir);
    312 
    313   // Append '/' to Opts.mOutputDir if not presents
    314   if (!OutputFile.empty() &&
    315       (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
    316     OutputFile.append(1, OS_PATH_SEPARATOR);
    317 
    318   if (OutputType == slang::Slang::OT_Dependency) {
    319     // The build system wants the .d file name stem to be exactly the same as
    320     // the source .rs file, instead of the .bc file.
    321     OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
    322   } else {
    323     OutputFile.append(
    324         slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
    325   }
    326 
    327   switch (OutputType) {
    328     case slang::Slang::OT_Dependency: {
    329       OutputFile.append(".d");
    330       break;
    331     }
    332     case slang::Slang::OT_Assembly: {
    333       OutputFile.append(".S");
    334       break;
    335     }
    336     case slang::Slang::OT_LLVMAssembly: {
    337       OutputFile.append(".ll");
    338       break;
    339     }
    340     case slang::Slang::OT_Object: {
    341       OutputFile.append(".o");
    342       break;
    343     }
    344     case slang::Slang::OT_Bitcode: {
    345       OutputFile.append(".bc");
    346       break;
    347     }
    348     case slang::Slang::OT_Nothing:
    349     default: {
    350       slangAssert(false && "Invalid output type!");
    351     }
    352   }
    353 
    354   return SaveStringInSet(SavedStrings, OutputFile);
    355 }
    356 
    357 #define str(s) #s
    358 #define wrap_str(s) str(s)
    359 static void llvm_rs_cc_VersionPrinter() {
    360   llvm::raw_ostream &OS = llvm::outs();
    361   OS << "llvm-rs-cc: Renderscript compiler\n"
    362      << "  (http://developer.android.com/guide/topics/renderscript)\n"
    363      << "  based on LLVM (http://llvm.org):\n";
    364   OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
    365   OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
    366      << SLANG_MAXIMUM_TARGET_API;
    367   OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
    368 #ifndef __DISABLE_ASSERTS
    369   OS << " with assertions";
    370 #endif
    371   OS << ".\n";
    372   return;
    373 }
    374 #undef wrap_str
    375 #undef str
    376 
    377 int main(int argc, const char **argv) {
    378   std::set<std::string> SavedStrings;
    379   llvm::SmallVector<const char*, 256> ArgVector;
    380   RSCCOptions Opts;
    381   llvm::SmallVector<const char*, 16> Inputs;
    382   std::string Argv0;
    383 
    384   atexit(llvm::llvm_shutdown);
    385 
    386   ExpandArgv(argc, argv, ArgVector, SavedStrings);
    387 
    388   // Argv0
    389   Argv0 = llvm::sys::path::stem(ArgVector[0]);
    390 
    391   // Setup diagnostic engine
    392   slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer();
    393 
    394   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
    395     new clang::DiagnosticIDs());
    396 
    397   clang::DiagnosticsEngine DiagEngine(DiagIDs, DiagClient, true);
    398 
    399   slang::Slang::GlobalInitialization();
    400 
    401   ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
    402 
    403   // Exits when there's any error occurred during parsing the arguments
    404   if (DiagEngine.hasErrorOccurred()) {
    405     llvm::errs() << DiagClient->str();
    406     return 1;
    407   }
    408 
    409   if (Opts.mShowHelp) {
    410     llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable());
    411     OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
    412                       "Renderscript source compiler");
    413     return 0;
    414   }
    415 
    416   if (Opts.mShowVersion) {
    417     llvm_rs_cc_VersionPrinter();
    418     return 0;
    419   }
    420 
    421   // No input file
    422   if (Inputs.empty()) {
    423     DiagEngine.Report(clang::diag::err_drv_no_input_files);
    424     llvm::errs() << DiagClient->str();
    425     return 1;
    426   }
    427 
    428   // Prepare input data for RS compiler.
    429   std::list<std::pair<const char*, const char*> > IOFiles;
    430   std::list<std::pair<const char*, const char*> > DepFiles;
    431 
    432   llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS());
    433 
    434   Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures, &DiagEngine,
    435                  DiagClient);
    436 
    437   for (int i = 0, e = Inputs.size(); i != e; i++) {
    438     const char *InputFile = Inputs[i];
    439     const char *OutputFile =
    440         DetermineOutputFile(Opts.mOutputDir, InputFile,
    441                             Opts.mOutputType, SavedStrings);
    442 
    443     if (Opts.mOutputDep) {
    444       const char *BCOutputFile, *DepOutputFile;
    445 
    446       if (Opts.mOutputType == slang::Slang::OT_Bitcode)
    447         BCOutputFile = OutputFile;
    448       else
    449         BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
    450                                            InputFile,
    451                                            slang::Slang::OT_Bitcode,
    452                                            SavedStrings);
    453 
    454       if (Opts.mOutputType == slang::Slang::OT_Dependency)
    455         DepOutputFile = OutputFile;
    456       else
    457         DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
    458                                             InputFile,
    459                                             slang::Slang::OT_Dependency,
    460                                             SavedStrings);
    461 
    462       DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
    463     }
    464 
    465     IOFiles.push_back(std::make_pair(InputFile, OutputFile));
    466   }
    467 
    468   // Let's rock!
    469   int CompileFailed = !Compiler->compile(IOFiles,
    470                                          DepFiles,
    471                                          Opts.mIncludePaths,
    472                                          Opts.mAdditionalDepTargets,
    473                                          Opts.mOutputType,
    474                                          Opts.mBitcodeStorage,
    475                                          Opts.mAllowRSPrefix,
    476                                          Opts.mOutputDep,
    477                                          Opts.mTargetAPI,
    478                                          Opts.mDebugEmission,
    479                                          Opts.mOptimizationLevel,
    480                                          Opts.mJavaReflectionPathBase,
    481                                          Opts.mJavaReflectionPackageName);
    482 
    483   Compiler->reset();
    484 
    485   return CompileFailed;
    486 }
    487 
    488 ///////////////////////////////////////////////////////////////////////////////
    489 
    490 // ExpandArgsFromBuf -
    491 static void ExpandArgsFromBuf(const char *Arg,
    492                               llvm::SmallVectorImpl<const char*> &ArgVector,
    493                               std::set<std::string> &SavedStrings) {
    494   const char *FName = Arg + 1;
    495   llvm::OwningPtr<llvm::MemoryBuffer> MemBuf;
    496   if (llvm::MemoryBuffer::getFile(FName, MemBuf)) {
    497     // Unable to open the file
    498     ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
    499     return;
    500   }
    501 
    502   const char *Buf = MemBuf->getBufferStart();
    503   char InQuote = ' ';
    504   std::string CurArg;
    505 
    506   for (const char *P = Buf; ; ++P) {
    507     if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
    508       if (!CurArg.empty()) {
    509         if (CurArg[0] != '@') {
    510           ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
    511         } else {
    512           ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
    513         }
    514 
    515         CurArg = "";
    516       }
    517       if (*P == '\0')
    518         break;
    519       else
    520         continue;
    521     }
    522 
    523     if (isspace(*P)) {
    524       if (InQuote != ' ')
    525         CurArg.push_back(*P);
    526       continue;
    527     }
    528 
    529     if (*P == '"' || *P == '\'') {
    530       if (InQuote == *P)
    531         InQuote = ' ';
    532       else if (InQuote == ' ')
    533         InQuote = *P;
    534       else
    535         CurArg.push_back(*P);
    536       continue;
    537     }
    538 
    539     if (*P == '\\') {
    540       ++P;
    541       if (*P != '\0')
    542         CurArg.push_back(*P);
    543       continue;
    544     }
    545     CurArg.push_back(*P);
    546   }
    547 }
    548 
    549 // ExpandArgsFromBuf -
    550 static void ExpandArgv(int argc, const char **argv,
    551                        llvm::SmallVectorImpl<const char*> &ArgVector,
    552                        std::set<std::string> &SavedStrings) {
    553   for (int i = 0; i < argc; ++i) {
    554     const char *Arg = argv[i];
    555     if (Arg[0] != '@') {
    556       ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
    557       continue;
    558     }
    559 
    560     ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
    561   }
    562 }
    563