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