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