• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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().
SaveStringInSet(std::set<std::string> & SavedStrings,llvm::StringRef S)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:
RSCCOptTable()95   RSCCOptTable()
96       : OptTable(RSCCInfoTable,
97                  sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {
98   }
99 };
100 
createRSCCOptTable()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 
RSCCOptions()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 -
ParseArguments(llvm::SmallVectorImpl<const char * > & ArgVector,llvm::SmallVectorImpl<const char * > & Inputs,RSCCOptions & Opts,clang::DiagnosticsEngine & DiagEngine)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 
DetermineOutputFile(const std::string & OutputDir,const char * InputFile,slang::Slang::OutputType OutputType,std::set<std::string> & SavedStrings)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)
llvm_rs_cc_VersionPrinter()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 
main(int argc,const char ** argv)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 -
ExpandArgsFromBuf(const char * Arg,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)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 -
ExpandArgv(int argc,const char ** argv,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)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