llvm-rs-cc.cpp revision e3cb21bc8654181da174ac441acec7726b5a78ec
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.
53using clang::driver::arg_iterator;
54using clang::driver::options::DriverOption;
55using clang::driver::Arg;
56using clang::driver::ArgList;
57using clang::driver::InputArgList;
58using clang::driver::Option;
59using 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().
64static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
65                                          llvm::StringRef S) {
66  return SavedStrings.insert(S).first->c_str();
67}
68static void ExpandArgsFromBuf(const char *Arg,
69                              llvm::SmallVectorImpl<const char*> &ArgVector,
70                              std::set<std::string> &SavedStrings);
71static void ExpandArgv(int argc, const char **argv,
72                       llvm::SmallVectorImpl<const char*> &ArgVector,
73                       std::set<std::string> &SavedStrings);
74
75enum {
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
84static 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
92class RSCCOptTable : public OptTable {
93 public:
94  RSCCOptTable()
95      : OptTable(RSCCInfoTable,
96                 sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {
97  }
98};
99
100OptTable *createRSCCOptTable() {
101  return new RSCCOptTable();
102}
103
104///////////////////////////////////////////////////////////////////////////////
105
106class 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
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 -
170static 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    Opts.mOutputDepDir =
277        Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir);
278    Opts.mAdditionalDepTargets =
279        Args->getAllArgValues(OPT_additional_dep_target);
280
281    Opts.mShowHelp = Args->hasArg(OPT_help);
282    Opts.mShowVersion = Args->hasArg(OPT_version);
283    Opts.mDebugEmission = Args->hasArg(OPT_emit_g);
284
285    size_t OptLevel = Args->getLastArgIntValue(OPT_optimization_level,
286                                               3,
287                                               DiagEngine);
288
289    Opts.mOptimizationLevel = OptLevel == 0 ? llvm::CodeGenOpt::None
290                                            : llvm::CodeGenOpt::Aggressive;
291
292    Opts.mTargetAPI = Args->getLastArgIntValue(OPT_target_api,
293                                               RS_VERSION,
294                                               DiagEngine);
295  }
296
297  return;
298}
299
300static const char *DetermineOutputFile(const std::string &OutputDir,
301                                       const char *InputFile,
302                                       slang::Slang::OutputType OutputType,
303                                       std::set<std::string> &SavedStrings) {
304  if (OutputType == slang::Slang::OT_Nothing)
305    return "/dev/null";
306
307  std::string OutputFile(OutputDir);
308
309  // Append '/' to Opts.mOutputDir if not presents
310  if (!OutputFile.empty() &&
311      (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
312    OutputFile.append(1, OS_PATH_SEPARATOR);
313
314  if (OutputType == slang::Slang::OT_Dependency) {
315    // The build system wants the .d file name stem to be exactly the same as
316    // the source .rs file, instead of the .bc file.
317    OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
318  } else {
319    OutputFile.append(
320        slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
321  }
322
323  switch (OutputType) {
324    case slang::Slang::OT_Dependency: {
325      OutputFile.append(".d");
326      break;
327    }
328    case slang::Slang::OT_Assembly: {
329      OutputFile.append(".S");
330      break;
331    }
332    case slang::Slang::OT_LLVMAssembly: {
333      OutputFile.append(".ll");
334      break;
335    }
336    case slang::Slang::OT_Object: {
337      OutputFile.append(".o");
338      break;
339    }
340    case slang::Slang::OT_Bitcode: {
341      OutputFile.append(".bc");
342      break;
343    }
344    case slang::Slang::OT_Nothing:
345    default: {
346      slangAssert(false && "Invalid output type!");
347    }
348  }
349
350  return SaveStringInSet(SavedStrings, OutputFile);
351}
352
353#define str(s) #s
354#define wrap_str(s) str(s)
355static void llvm_rs_cc_VersionPrinter() {
356  llvm::raw_ostream &OS = llvm::outs();
357  OS << "llvm-rs-cc: Renderscript compiler\n"
358     << "  (http://developer.android.com/guide/topics/renderscript)\n"
359     << "  based on LLVM (http://llvm.org):\n";
360  OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
361  OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
362     << SLANG_MAXIMUM_TARGET_API;
363  OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
364#ifndef __DISABLE_ASSERTS
365  OS << " with assertions";
366#endif
367  OS << ".\n";
368  return;
369}
370#undef wrap_str
371#undef str
372
373int main(int argc, const char **argv) {
374  std::set<std::string> SavedStrings;
375  llvm::SmallVector<const char*, 256> ArgVector;
376  RSCCOptions Opts;
377  llvm::SmallVector<const char*, 16> Inputs;
378  std::string Argv0;
379
380  atexit(llvm::llvm_shutdown);
381
382  ExpandArgv(argc, argv, ArgVector, SavedStrings);
383
384  // Argv0
385  Argv0 = llvm::sys::path::stem(ArgVector[0]);
386
387  // Setup diagnostic engine
388  slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer();
389
390  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
391    new clang::DiagnosticIDs());
392
393  clang::DiagnosticsEngine DiagEngine(DiagIDs, DiagClient, true);
394
395  slang::Slang::GlobalInitialization();
396
397  ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
398
399  // Exits when there's any error occurred during parsing the arguments
400  if (DiagEngine.hasErrorOccurred()) {
401    llvm::errs() << DiagClient->str();
402    return 1;
403  }
404
405  if (Opts.mShowHelp) {
406    llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable());
407    OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
408                      "Renderscript source compiler");
409    return 0;
410  }
411
412  if (Opts.mShowVersion) {
413    llvm_rs_cc_VersionPrinter();
414    return 0;
415  }
416
417  // No input file
418  if (Inputs.empty()) {
419    DiagEngine.Report(clang::diag::err_drv_no_input_files);
420    llvm::errs() << DiagClient->str();
421    return 1;
422  }
423
424  // Prepare input data for RS compiler.
425  std::list<std::pair<const char*, const char*> > IOFiles;
426  std::list<std::pair<const char*, const char*> > DepFiles;
427
428  llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS());
429
430  Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures, &DiagEngine,
431                 DiagClient);
432
433  for (int i = 0, e = Inputs.size(); i != e; i++) {
434    const char *InputFile = Inputs[i];
435    const char *OutputFile =
436        DetermineOutputFile(Opts.mOutputDir, InputFile,
437                            Opts.mOutputType, SavedStrings);
438
439    if (Opts.mOutputDep) {
440      const char *BCOutputFile, *DepOutputFile;
441
442      if (Opts.mOutputType == slang::Slang::OT_Bitcode)
443        BCOutputFile = OutputFile;
444      else
445        BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
446                                           InputFile,
447                                           slang::Slang::OT_Bitcode,
448                                           SavedStrings);
449
450      if (Opts.mOutputType == slang::Slang::OT_Dependency)
451        DepOutputFile = OutputFile;
452      else
453        DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
454                                            InputFile,
455                                            slang::Slang::OT_Dependency,
456                                            SavedStrings);
457
458      DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
459    }
460
461    IOFiles.push_back(std::make_pair(InputFile, OutputFile));
462  }
463
464  // Let's rock!
465  int CompileFailed = !Compiler->compile(IOFiles,
466                                         DepFiles,
467                                         Opts.mIncludePaths,
468                                         Opts.mAdditionalDepTargets,
469                                         Opts.mOutputType,
470                                         Opts.mBitcodeStorage,
471                                         Opts.mAllowRSPrefix,
472                                         Opts.mOutputDep,
473                                         Opts.mTargetAPI,
474                                         Opts.mDebugEmission,
475                                         Opts.mOptimizationLevel,
476                                         Opts.mJavaReflectionPathBase,
477                                         Opts.mJavaReflectionPackageName);
478  Compiler->reset();
479
480  return CompileFailed;
481}
482
483///////////////////////////////////////////////////////////////////////////////
484
485// ExpandArgsFromBuf -
486static void ExpandArgsFromBuf(const char *Arg,
487                              llvm::SmallVectorImpl<const char*> &ArgVector,
488                              std::set<std::string> &SavedStrings) {
489  const char *FName = Arg + 1;
490  llvm::OwningPtr<llvm::MemoryBuffer> MemBuf;
491  if (llvm::MemoryBuffer::getFile(FName, MemBuf)) {
492    // Unable to open the file
493    ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
494    return;
495  }
496
497  const char *Buf = MemBuf->getBufferStart();
498  char InQuote = ' ';
499  std::string CurArg;
500
501  for (const char *P = Buf; ; ++P) {
502    if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
503      if (!CurArg.empty()) {
504        if (CurArg[0] != '@') {
505          ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
506        } else {
507          ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
508        }
509
510        CurArg = "";
511      }
512      if (*P == '\0')
513        break;
514      else
515        continue;
516    }
517
518    if (isspace(*P)) {
519      if (InQuote != ' ')
520        CurArg.push_back(*P);
521      continue;
522    }
523
524    if (*P == '"' || *P == '\'') {
525      if (InQuote == *P)
526        InQuote = ' ';
527      else if (InQuote == ' ')
528        InQuote = *P;
529      else
530        CurArg.push_back(*P);
531      continue;
532    }
533
534    if (*P == '\\') {
535      ++P;
536      if (*P != '\0')
537        CurArg.push_back(*P);
538      continue;
539    }
540    CurArg.push_back(*P);
541  }
542}
543
544// ExpandArgsFromBuf -
545static void ExpandArgv(int argc, const char **argv,
546                       llvm::SmallVectorImpl<const char*> &ArgVector,
547                       std::set<std::string> &SavedStrings) {
548  for (int i = 0; i < argc; ++i) {
549    const char *Arg = argv[i];
550    if (Arg[0] != '@') {
551      ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
552      continue;
553    }
554
555    ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
556  }
557}
558