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/CompilerInvocation.h"
21#include "clang/Frontend/FrontendDiagnostic.h"
22#include "clang/Frontend/TextDiagnosticPrinter.h"
23#include "clang/Frontend/Utils.h"
24
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/ADT/IntrusiveRefCntPtr.h"
27
28#include "llvm/Option/OptTable.h"
29#include "llvm/Support/Allocator.h"
30#include "llvm/Support/ManagedStatic.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/Support/Path.h"
33#include "llvm/Support/raw_ostream.h"
34#include "llvm/Support/Signals.h"
35#include "llvm/Support/StringSaver.h"
36#include "llvm/Target/TargetMachine.h"
37
38#include "os_sep.h"
39#include "rs_cc_options.h"
40#include "slang.h"
41#include "slang_assert.h"
42#include "slang_diagnostic_buffer.h"
43#include "slang_rs_reflect_utils.h"
44
45#include <list>
46#include <set>
47#include <string>
48
49namespace {
50class StringSet {
51public:
52  const char *save(const char *Str) {
53    return Strings.save(Str);
54  }
55
56  StringSet() : Strings(A), A() {}
57
58  llvm::StringSaver & getStringSaver() { return Strings; }
59
60private:
61  llvm::StringSaver Strings;
62  llvm::BumpPtrAllocator A;
63};
64}
65
66static const char *DetermineOutputFile(const std::string &OutputDir,
67                                       const std::string &PathSuffix,
68                                       const char *InputFile,
69                                       slang::Slang::OutputType OutputType,
70                                       StringSet *SavedStrings) {
71  if (OutputType == slang::Slang::OT_Nothing)
72    return "/dev/null";
73
74  std::string OutputFile(OutputDir);
75
76  // Append '/' to Opts.mBitcodeOutputDir if not presents
77  if (!OutputFile.empty() &&
78      (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
79    OutputFile.append(1, OS_PATH_SEPARATOR);
80
81  if (!PathSuffix.empty()) {
82    OutputFile.append(PathSuffix);
83    OutputFile.append(1, OS_PATH_SEPARATOR);
84  }
85
86  if (OutputType == slang::Slang::OT_Dependency) {
87    // The build system wants the .d file name stem to be exactly the same as
88    // the source .rs file, instead of the .bc file.
89    OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
90  } else {
91    OutputFile.append(
92        slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
93  }
94
95  switch (OutputType) {
96    case slang::Slang::OT_Dependency: {
97      OutputFile.append(".d");
98      break;
99    }
100    case slang::Slang::OT_Assembly: {
101      OutputFile.append(".S");
102      break;
103    }
104    case slang::Slang::OT_LLVMAssembly: {
105      OutputFile.append(".ll");
106      break;
107    }
108    case slang::Slang::OT_Object: {
109      OutputFile.append(".o");
110      break;
111    }
112    case slang::Slang::OT_Bitcode: {
113      OutputFile.append(".bc");
114      break;
115    }
116    case slang::Slang::OT_Nothing:
117    default: {
118      slangAssert(false && "Invalid output type!");
119    }
120  }
121
122  return SavedStrings->save(OutputFile.c_str());
123}
124
125typedef std::list<std::pair<const char*, const char*> > NamePairList;
126
127/*
128 * Compile the Inputs.
129 *
130 * Returns 0 on success and nonzero on failure.
131 *
132 * IOFiles - list of (foo.rs, foo.bc) pairs of input/output files.
133 * IOFiles32 - list of input/output pairs for 32-bit compilation.
134 * Inputs - input filenames.
135 * Opts - options controlling compilation.
136 * DiagEngine - Clang diagnostic engine (for creating diagnostics).
137 * DiagClient - Slang diagnostic consumer (collects and displays diagnostics).
138 * SavedStrings - expanded strings copied from argv source input files.
139 *
140 * We populate IOFiles dynamically while working through the list of Inputs.
141 * On any 64-bit compilation, we pass back in the 32-bit pairs of files as
142 * IOFiles32. This allows the 64-bit compiler to later bundle up both the
143 * 32-bit and 64-bit bitcode outputs to be included in the final reflected
144 * source code that is emitted.
145 */
146static void makeFileList(NamePairList *IOFiles, NamePairList *DepFiles,
147    const llvm::SmallVector<const char*, 16> &Inputs, slang::RSCCOptions &Opts,
148    StringSet *SavedStrings) {
149  std::string PathSuffix = "";
150  // In our mixed 32/64-bit path, we need to suffix our files differently for
151  // both 32-bit and 64-bit versions.
152  if (Opts.mEmit3264) {
153    if (Opts.mBitWidth == 64) {
154      PathSuffix = "bc64";
155    } else {
156      PathSuffix = "bc32";
157    }
158  }
159
160  for (int i = 0, e = Inputs.size(); i != e; i++) {
161    const char *InputFile = Inputs[i];
162
163    const char *BCOutputFile = DetermineOutputFile(Opts.mBitcodeOutputDir,
164                                                   PathSuffix, InputFile,
165                                                   Opts.mOutputType,
166                                                   SavedStrings);
167    const char *OutputFile = BCOutputFile;
168
169    if (Opts.mEmitDependency) {
170      // The dependency file is always emitted without a PathSuffix.
171      // Collisions between 32-bit and 64-bit files don't make a difference,
172      // because they share the same sources/dependencies.
173      const char *DepOutputFile =
174          DetermineOutputFile(Opts.mDependencyOutputDir, "", InputFile,
175                              slang::Slang::OT_Dependency, SavedStrings);
176      if (Opts.mOutputType == slang::Slang::OT_Dependency) {
177        OutputFile = DepOutputFile;
178      }
179
180      DepFiles->push_back(std::make_pair(BCOutputFile, DepOutputFile));
181    }
182
183    IOFiles->push_back(std::make_pair(InputFile, OutputFile));
184  }
185}
186
187#define str(s) #s
188#define wrap_str(s) str(s)
189static void llvm_rs_cc_VersionPrinter() {
190  llvm::raw_ostream &OS = llvm::outs();
191  OS << "llvm-rs-cc: Renderscript compiler\n"
192     << "  (http://developer.android.com/guide/topics/renderscript)\n"
193     << "  based on LLVM (http://llvm.org):\n";
194  OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
195  OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
196     << SLANG_MAXIMUM_TARGET_API;
197  OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
198#ifndef __DISABLE_ASSERTS
199  OS << " with assertions";
200#endif
201  OS << ".\n";
202}
203#undef wrap_str
204#undef str
205
206static void LLVMErrorHandler(void *UserData, const std::string &Message,
207                             bool GenCrashDialog) {
208  clang::DiagnosticsEngine *DiagEngine =
209      static_cast<clang::DiagnosticsEngine *>(UserData);
210
211  DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
212
213  // Run the interrupt handlers to make sure any special cleanups get done, in
214  // particular that we remove files registered with RemoveFileOnSignal.
215  llvm::sys::RunInterruptHandlers();
216
217  exit(1);
218}
219
220int main(int argc, const char **argv) {
221  llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
222  LLVMInitializeARMTargetInfo();
223  LLVMInitializeARMTarget();
224  LLVMInitializeARMAsmPrinter();
225
226  StringSet SavedStrings; // Keeps track of strings to be destroyed at the end.
227
228  // Parse the command line arguments and respond to show help & version
229  // commands.
230  llvm::SmallVector<const char *, 16> Inputs;
231  slang::RSCCOptions Opts;
232  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts =
233      new clang::DiagnosticOptions();
234  if (!slang::ParseArguments(llvm::makeArrayRef(argv, argc), Inputs, Opts,
235                             *DiagOpts, SavedStrings.getStringSaver())) {
236    // Exits when there's any error occurred during parsing the arguments
237    return 1;
238  }
239  if (Opts.mShowHelp) {
240    std::unique_ptr<llvm::opt::OptTable> OptTbl(slang::createRSCCOptTable());
241    const std::string Argv0 = llvm::sys::path::stem(argv[0]);
242    OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
243                      "Renderscript source compiler");
244    return 0;
245  }
246  if (Opts.mShowVersion) {
247    llvm_rs_cc_VersionPrinter();
248    return 0;
249  }
250
251  // Initialize the diagnostic objects
252  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
253      new clang::DiagnosticIDs());
254  slang::DiagnosticBuffer DiagsBuffer;
255  clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, &DiagsBuffer, false);
256  clang::ProcessWarningOptions(DiagEngine, *DiagOpts);
257  (void)DiagEngine.setSeverityForGroup(clang::diag::Flavor::WarningOrError,
258                                       "implicit-function-declaration",
259                                       clang::diag::Severity::Error);
260
261  // Report error if no input file
262  if (Inputs.empty()) {
263    DiagEngine.Report(clang::diag::err_drv_no_input_files);
264    llvm::errs() << DiagsBuffer.str();
265    return 1;
266  }
267
268  llvm::install_fatal_error_handler(LLVMErrorHandler, &DiagEngine);
269
270  // Compile the 32 bit version
271  NamePairList IOFiles32;
272  NamePairList DepFiles32;
273  makeFileList(&IOFiles32, &DepFiles32, Inputs, Opts, &SavedStrings);
274
275  int CompileFailed = 0;
276  // Handle 32-bit case for Java and C++ reflection.
277  // For Java, both 32bit and 64bit will be generated.
278  // For C++, either 64bit or 32bit will be generated based on the target.
279  if (Opts.mEmit3264 || Opts.mBitWidth == 32) {
280      std::unique_ptr<slang::Slang> Compiler(
281          new slang::Slang(32, &DiagEngine, &DiagsBuffer));
282      CompileFailed =
283          !Compiler->compile(IOFiles32, IOFiles32, DepFiles32, Opts, *DiagOpts);
284  }
285
286  // Handle the 64-bit case too!
287  bool needEmit64 = Opts.mEmit3264 || Opts.mBitWidth == 64;
288  if (needEmit64 && !CompileFailed) {
289    Opts.mBitWidth = 64;
290    NamePairList IOFiles64;
291    NamePairList DepFiles64;
292    makeFileList(&IOFiles64, &DepFiles64, Inputs, Opts, &SavedStrings);
293
294    std::unique_ptr<slang::Slang> Compiler(
295        new slang::Slang(64, &DiagEngine, &DiagsBuffer));
296    CompileFailed =
297        !Compiler->compile(IOFiles64, IOFiles32, DepFiles64, Opts, *DiagOpts);
298  }
299
300  llvm::errs() << DiagsBuffer.str();
301  llvm::remove_fatal_error_handler();
302  return CompileFailed;
303}
304