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