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