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