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