llvm-rs-cc.cpp revision 76837a1d0d86300c09c093b535d5fc4b638f6805
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 "clang/Basic/DiagnosticOptions.h"
18#include "clang/Driver/DriverDiagnostic.h"
19#include "clang/Driver/Options.h"
20#include "clang/Frontend/TextDiagnosticPrinter.h"
21#include "clang/Frontend/Utils.h"
22
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/IntrusiveRefCntPtr.h"
25
26#include "llvm/Option/OptTable.h"
27#include "llvm/Support/CommandLine.h"
28#include "llvm/Support/ManagedStatic.h"
29#include "llvm/Support/MemoryBuffer.h"
30#include "llvm/Support/Path.h"
31#include "llvm/Support/raw_ostream.h"
32#include "llvm/Target/TargetMachine.h"
33
34#include "rs_cc_options.h"
35#include "slang.h"
36#include "slang_assert.h"
37#include "slang_diagnostic_buffer.h"
38#include "slang_rs.h"
39#include "slang_rs_reflect_utils.h"
40
41#include <list>
42#include <set>
43#include <string>
44
45// SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from
46// $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in
47// main().
48static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
49                                          llvm::StringRef S) {
50  return SavedStrings.insert(S).first->c_str();
51}
52static void ExpandArgsFromBuf(const char *Arg,
53                              llvm::SmallVectorImpl<const char*> &ArgVector,
54                              std::set<std::string> &SavedStrings);
55static void ExpandArgv(int argc, const char **argv,
56                       llvm::SmallVectorImpl<const char*> &ArgVector,
57                       std::set<std::string> &SavedStrings);
58
59static const char *DetermineOutputFile(const std::string &OutputDir,
60                                       const std::string &PathSuffix,
61                                       const char *InputFile,
62                                       slang::Slang::OutputType OutputType,
63                                       std::set<std::string> &SavedStrings) {
64  if (OutputType == slang::Slang::OT_Nothing)
65    return "/dev/null";
66
67  std::string OutputFile(OutputDir);
68
69  // Append '/' to Opts.mBitcodeOutputDir if not presents
70  if (!OutputFile.empty() &&
71      (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
72    OutputFile.append(1, OS_PATH_SEPARATOR);
73
74  if (!PathSuffix.empty()) {
75    OutputFile.append(PathSuffix);
76    OutputFile.append(1, OS_PATH_SEPARATOR);
77  }
78
79  if (OutputType == slang::Slang::OT_Dependency) {
80    // The build system wants the .d file name stem to be exactly the same as
81    // the source .rs file, instead of the .bc file.
82    OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
83  } else {
84    OutputFile.append(
85        slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
86  }
87
88  switch (OutputType) {
89    case slang::Slang::OT_Dependency: {
90      OutputFile.append(".d");
91      break;
92    }
93    case slang::Slang::OT_Assembly: {
94      OutputFile.append(".S");
95      break;
96    }
97    case slang::Slang::OT_LLVMAssembly: {
98      OutputFile.append(".ll");
99      break;
100    }
101    case slang::Slang::OT_Object: {
102      OutputFile.append(".o");
103      break;
104    }
105    case slang::Slang::OT_Bitcode: {
106      OutputFile.append(".bc");
107      break;
108    }
109    case slang::Slang::OT_Nothing:
110    default: {
111      slangAssert(false && "Invalid output type!");
112    }
113  }
114
115  return SaveStringInSet(SavedStrings, OutputFile);
116}
117
118typedef std::list<std::pair<const char*, const char*> > NamePairList;
119
120/*
121 * Compile the Inputs.
122 *
123 * Returns 0 on success and nonzero on failure.
124 *
125 * IOFiles - list of (foo.rs, foo.bc) pairs of input/output files.
126 * IOFiles32 - list of input/output pairs for 32-bit compilation.
127 * Inputs - input filenames.
128 * Opts - options controlling compilation.
129 * DiagEngine - Clang diagnostic engine (for creating diagnostics).
130 * DiagClient - Slang diagnostic consumer (collects and displays diagnostics).
131 * SavedStrings - expanded strings copied from argv source input files.
132 *
133 * We populate IOFiles dynamically while working through the list of Inputs.
134 * On any 64-bit compilation, we pass back in the 32-bit pairs of files as
135 * IOFiles32. This allows the 64-bit compiler to later bundle up both the
136 * 32-bit and 64-bit bitcode outputs to be included in the final reflected
137 * source code that is emitted.
138 */
139static int compileFiles(NamePairList *IOFiles, NamePairList *IOFiles32,
140    const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts,
141    clang::DiagnosticsEngine *DiagEngine, slang::DiagnosticBuffer *DiagClient,
142    std::set<std::string> *SavedStrings) {
143  NamePairList DepFiles;
144  std::string PathSuffix = "";
145  bool CompileSecondTimeFor64Bit = false;
146
147  // In our mixed 32/64-bit path, we need to suffix our files differently for
148  // both 32-bit and 64-bit versions.
149  if (Opts.mEmit3264) {
150    if (Opts.mBitWidth == 64) {
151      PathSuffix = "bc64";
152      CompileSecondTimeFor64Bit = true;
153    } else {
154      PathSuffix = "bc32";
155    }
156  }
157
158  for (int i = 0, e = Inputs.size(); i != e; i++) {
159    const char *InputFile = Inputs[i];
160
161    const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir,
162                                                   PathSuffix, InputFile,
163                                                   Opts.mOutputType,
164                                                   *SavedStrings);
165    const char *OutputFile = BCOutputFile;
166
167    if (Opts.mEmitDependency) {
168      // The dependency file is always emitted without a PathSuffix.
169      // Collisions between 32-bit and 64-bit files don't make a difference,
170      // because they share the same sources/dependencies.
171      const char *DepOutputFile =
172          DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile,
173                              slang::Slang::OT_Dependency, *SavedStrings);
174      if (Opts.mOutputType == slang::Slang::OT_Dependency) {
175        OutputFile = DepOutputFile;
176      }
177
178      DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
179    }
180
181    IOFiles->push_back(std::make_pair(InputFile, OutputFile));
182  }
183
184  std::unique_ptr<slang::SlangRS> Compiler(new slang::SlangRS());
185  Compiler->init(Opts.mBitWidth, DiagEngine, DiagClient);
186  int CompileFailed = !Compiler->compile(*IOFiles, *IOFiles32, DepFiles, Opts);
187  // We suppress warnings (via reset) if we are doing a second compilation.
188  Compiler->reset(CompileSecondTimeFor64Bit);
189  return CompileFailed;
190}
191
192#define str(s) #s
193#define wrap_str(s) str(s)
194static void llvm_rs_cc_VersionPrinter() {
195  llvm::raw_ostream &OS = llvm::outs();
196  OS << "llvm-rs-cc: Renderscript compiler\n"
197     << "  (http://developer.android.com/guide/topics/renderscript)\n"
198     << "  based on LLVM (http://llvm.org):\n";
199  OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
200  OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
201     << SLANG_MAXIMUM_TARGET_API;
202  OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
203#ifndef __DISABLE_ASSERTS
204  OS << " with assertions";
205#endif
206  OS << ".\n";
207}
208#undef wrap_str
209#undef str
210
211int main(int argc, const char **argv) {
212  std::set<std::string> SavedStrings;
213  llvm::SmallVector<const char*, 256> ArgVector;
214  slang::RSCCOptions Opts;
215  llvm::SmallVector<const char*, 16> Inputs;
216  std::string Argv0;
217
218  llvm::llvm_shutdown_obj Y;  // Call llvm_shutdown() on exit.
219
220  ExpandArgv(argc, argv, ArgVector, SavedStrings);
221
222  // Argv0
223  Argv0 = llvm::sys::path::stem(ArgVector[0]);
224
225  // Setup diagnostic engine
226  slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer();
227
228  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
229    new clang::DiagnosticIDs());
230
231  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
232    new clang::DiagnosticOptions());
233  clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, DiagClient, true);
234
235  slang::Slang::GlobalInitialization();
236
237  slang::ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
238
239  // Exits when there's any error occurred during parsing the arguments
240  if (DiagEngine.hasErrorOccurred()) {
241    llvm::errs() << DiagClient->str();
242    return 1;
243  }
244
245  if (Opts.mShowHelp) {
246    std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable());
247    OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
248                      "Renderscript source compiler");
249    return 0;
250  }
251
252  if (Opts.mShowVersion) {
253    llvm_rs_cc_VersionPrinter();
254    return 0;
255  }
256
257  // No input file
258  if (Inputs.empty()) {
259    DiagEngine.Report(clang::diag::err_drv_no_input_files);
260    llvm::errs() << DiagClient->str();
261    return 1;
262  }
263
264  // Prepare input data for RS compiler.
265  NamePairList IOFiles64;
266  NamePairList IOFiles32;
267
268  int CompileFailed = compileFiles(&IOFiles32, &IOFiles32, Inputs, Opts,
269                                   &DiagEngine, DiagClient, &SavedStrings);
270
271  // Handle the 64-bit case too!
272  if (Opts.mEmit3264 && !CompileFailed) {
273    Opts.mBitWidth = 64;
274    CompileFailed = compileFiles(&IOFiles64, &IOFiles32, Inputs, Opts,
275                                 &DiagEngine, DiagClient, &SavedStrings);
276  }
277
278  return CompileFailed;
279}
280
281///////////////////////////////////////////////////////////////////////////////
282
283// ExpandArgsFromBuf -
284static void ExpandArgsFromBuf(const char *Arg,
285                              llvm::SmallVectorImpl<const char*> &ArgVector,
286                              std::set<std::string> &SavedStrings) {
287  const char *FName = Arg + 1;
288  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MBOrErr =
289      llvm::MemoryBuffer::getFile(FName);
290  if (MBOrErr.getError()) {
291    // Unable to open the file
292    ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
293    return;
294  }
295  std::unique_ptr<llvm::MemoryBuffer> MemBuf = std::move(MBOrErr.get());
296
297  const char *Buf = MemBuf->getBufferStart();
298  char InQuote = ' ';
299  std::string CurArg;
300
301  for (const char *P = Buf; ; ++P) {
302    if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
303      if (!CurArg.empty()) {
304        if (CurArg[0] != '@') {
305          ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
306        } else {
307          ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
308        }
309
310        CurArg = "";
311      }
312      if (*P == '\0')
313        break;
314      else
315        continue;
316    }
317
318    if (isspace(*P)) {
319      if (InQuote != ' ')
320        CurArg.push_back(*P);
321      continue;
322    }
323
324    if (*P == '"' || *P == '\'') {
325      if (InQuote == *P)
326        InQuote = ' ';
327      else if (InQuote == ' ')
328        InQuote = *P;
329      else
330        CurArg.push_back(*P);
331      continue;
332    }
333
334    if (*P == '\\') {
335      ++P;
336      if (*P != '\0')
337        CurArg.push_back(*P);
338      continue;
339    }
340    CurArg.push_back(*P);
341  }
342}
343
344// ExpandArgsFromBuf -
345static void ExpandArgv(int argc, const char **argv,
346                       llvm::SmallVectorImpl<const char*> &ArgVector,
347                       std::set<std::string> &SavedStrings) {
348  for (int i = 0; i < argc; ++i) {
349    const char *Arg = argv[i];
350    if (Arg[0] != '@') {
351      ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
352      continue;
353    }
354
355    ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
356  }
357}
358