• 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/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().
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 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:
RSCCOptTable()106   RSCCOptTable()
107       : OptTable(RSCCInfoTable,
108                  sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {
109   }
110 };
111 
createRSCCOptTable()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 
RSCCOptions()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 -
ParseArguments(llvm::SmallVectorImpl<const char * > & ArgVector,llvm::SmallVectorImpl<const char * > & Inputs,RSCCOptions & Opts,clang::DiagnosticsEngine & DiagEngine)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 
DetermineOutputFile(const std::string & OutputDir,const char * InputFile,slang::Slang::OutputType OutputType,std::set<std::string> & SavedStrings)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)
llvm_rs_cc_VersionPrinter()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 
main(int argc,const char ** argv)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 -
ExpandArgsFromBuf(const char * Arg,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)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 -
ExpandArgv(int argc,const char ** argv,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)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