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