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 "slang.h" 18 19 #include <stdlib.h> 20 21 #include <string> 22 #include <vector> 23 24 #include "clang/AST/ASTConsumer.h" 25 #include "clang/AST/ASTContext.h" 26 27 #include "clang/Basic/DiagnosticIDs.h" 28 #include "clang/Basic/FileManager.h" 29 #include "clang/Basic/FileSystemOptions.h" 30 #include "clang/Basic/LangOptions.h" 31 #include "clang/Basic/SourceManager.h" 32 #include "clang/Basic/TargetInfo.h" 33 #include "clang/Basic/TargetOptions.h" 34 35 #include "clang/Frontend/CodeGenOptions.h" 36 #include "clang/Frontend/DiagnosticOptions.h" 37 #include "clang/Frontend/DependencyOutputOptions.h" 38 #include "clang/Frontend/FrontendDiagnostic.h" 39 #include "clang/Frontend/TextDiagnosticPrinter.h" 40 #include "clang/Frontend/Utils.h" 41 42 #include "clang/Lex/Preprocessor.h" 43 #include "clang/Lex/HeaderSearch.h" 44 45 #include "clang/Parse/ParseAST.h" 46 47 #include "llvm/ADT/IntrusiveRefCntPtr.h" 48 49 #include "llvm/Bitcode/ReaderWriter.h" 50 51 // More force linking 52 #include "llvm/Linker.h" 53 54 // Force linking all passes/vmcore stuffs to libslang.so 55 #include "llvm/LinkAllPasses.h" 56 #include "llvm/LinkAllVMCore.h" 57 58 #include "llvm/Support/raw_ostream.h" 59 #include "llvm/Support/MemoryBuffer.h" 60 #include "llvm/Support/ErrorHandling.h" 61 #include "llvm/Support/ManagedStatic.h" 62 #include "llvm/Support/Path.h" 63 #include "llvm/Support/TargetSelect.h" 64 #include "llvm/Support/ToolOutputFile.h" 65 66 #include "slang_assert.h" 67 #include "slang_backend.h" 68 #include "slang_utils.h" 69 70 namespace { 71 72 struct ForceSlangLinking { 73 ForceSlangLinking() { 74 // We must reference the functions in such a way that compilers will not 75 // delete it all as dead code, even with whole program optimization, 76 // yet is effectively a NO-OP. As the compiler isn't smart enough 77 // to know that getenv() never returns -1, this will do the job. 78 if (std::getenv("bar") != reinterpret_cast<char*>(-1)) 79 return; 80 81 // llvm-rs-link needs following functions existing in libslang. 82 llvm::ParseBitcodeFile(NULL, llvm::getGlobalContext(), NULL); 83 llvm::Linker::LinkModules(NULL, NULL, 0, NULL); 84 85 // llvm-rs-cc need this. 86 new clang::TextDiagnosticPrinter(llvm::errs(), 87 clang::DiagnosticOptions()); 88 } 89 } ForceSlangLinking; 90 91 } // namespace 92 93 namespace slang { 94 95 #if defined(__arm__) 96 # define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi" 97 #elif defined(__x86_64__) 98 # define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux" 99 #else 100 // let's use x86 as default target 101 # define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux" 102 #endif 103 104 bool Slang::GlobalInitialized = false; 105 106 // Language option (define the language feature for compiler such as C99) 107 clang::LangOptions Slang::LangOpts; 108 109 // Code generation option for the compiler 110 clang::CodeGenOptions Slang::CodeGenOpts; 111 112 // The named of metadata node that pragma resides (should be synced with 113 // bcc.cpp) 114 const llvm::StringRef Slang::PragmaMetadataName = "#pragma"; 115 116 static inline llvm::tool_output_file * 117 OpenOutputFile(const char *OutputFile, 118 unsigned Flags, 119 std::string* Error, 120 clang::DiagnosticsEngine *DiagEngine) 121 { 122 slangAssert((OutputFile != NULL) && (Error != NULL) && 123 (DiagEngine != NULL) && "Invalid parameter!"); 124 125 if (SlangUtils::CreateDirectoryWithParents( 126 llvm::sys::path::parent_path(OutputFile), Error)) { 127 llvm::tool_output_file *F = 128 new llvm::tool_output_file(OutputFile, *Error, Flags); 129 if (F != NULL) 130 return F; 131 } 132 133 // Report error here. 134 DiagEngine->Report(clang::diag::err_fe_error_opening) 135 << OutputFile << *Error; 136 137 return NULL; 138 } 139 140 void Slang::GlobalInitialization() { 141 if (!GlobalInitialized) { 142 // We only support x86, x64 and ARM target 143 144 // For ARM 145 LLVMInitializeARMTargetInfo(); 146 LLVMInitializeARMTarget(); 147 LLVMInitializeARMAsmPrinter(); 148 149 // For x86 and x64 150 LLVMInitializeX86TargetInfo(); 151 LLVMInitializeX86Target(); 152 LLVMInitializeX86AsmPrinter(); 153 154 // Please refer to include/clang/Basic/LangOptions.h to setup 155 // the options. 156 LangOpts.RTTI = 0; // Turn off the RTTI information support 157 LangOpts.NeXTRuntime = 0; // Turn off the NeXT runtime uses 158 LangOpts.C99 = 1; 159 160 CodeGenOpts.OptimizationLevel = 3; /* -O3 */ 161 162 GlobalInitialized = true; 163 } 164 } 165 166 void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) { 167 clang::DiagnosticsEngine* DiagEngine = 168 static_cast<clang::DiagnosticsEngine *>(UserData); 169 170 DiagEngine->Report(clang::diag::err_fe_error_backend) << Message; 171 exit(1); 172 } 173 174 void Slang::createDiagnostic() { 175 mDiagClient = new DiagnosticBuffer(); 176 177 mDiagIDs = new clang::DiagnosticIDs(); 178 mDiagEngine = new clang::DiagnosticsEngine(mDiagIDs, mDiagClient, true); 179 mDiag.reset(new clang::Diagnostic(mDiagEngine.getPtr())); 180 181 initDiagnostic(); 182 } 183 184 void Slang::createTarget(const std::string &Triple, const std::string &CPU, 185 const std::vector<std::string> &Features) { 186 if (!Triple.empty()) 187 mTargetOpts.Triple = Triple; 188 else 189 mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING; 190 191 if (!CPU.empty()) 192 mTargetOpts.CPU = CPU; 193 194 if (!Features.empty()) 195 mTargetOpts.Features = Features; 196 197 mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine, 198 mTargetOpts)); 199 } 200 201 void Slang::createFileManager() { 202 mFileSysOpt.reset(new clang::FileSystemOptions()); 203 mFileMgr.reset(new clang::FileManager(*mFileSysOpt)); 204 } 205 206 void Slang::createSourceManager() { 207 mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr)); 208 } 209 210 void Slang::createPreprocessor() { 211 // Default only search header file in current dir 212 clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(*mFileMgr); 213 214 mPP.reset(new clang::Preprocessor(*mDiagEngine, 215 LangOpts, 216 mTarget.get(), 217 *mSourceMgr, 218 *HeaderInfo, 219 *this, 220 NULL, 221 /* OwnsHeaderSearch = */true)); 222 // Initialize the preprocessor 223 mPragmas.clear(); 224 mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas)); 225 226 std::vector<clang::DirectoryLookup> SearchList; 227 for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) { 228 if (const clang::DirectoryEntry *DE = 229 mFileMgr->getDirectory(mIncludePaths[i])) { 230 SearchList.push_back(clang::DirectoryLookup(DE, 231 clang::SrcMgr::C_System, 232 false, 233 false)); 234 } 235 } 236 237 HeaderInfo->SetSearchPaths(SearchList, 238 /* angledDirIdx = */1, 239 /* systemDixIdx = */1, 240 /* noCurDirSearch = */false); 241 242 initPreprocessor(); 243 } 244 245 void Slang::createASTContext() { 246 mASTContext.reset(new clang::ASTContext(LangOpts, 247 *mSourceMgr, 248 mTarget.get(), 249 mPP->getIdentifierTable(), 250 mPP->getSelectorTable(), 251 mPP->getBuiltinInfo(), 252 /* size_reserve = */0)); 253 initASTContext(); 254 } 255 256 clang::ASTConsumer * 257 Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts, 258 llvm::raw_ostream *OS, OutputType OT) { 259 return new Backend(mDiagEngine.getPtr(), CodeGenOpts, mTargetOpts, 260 &mPragmas, OS, OT); 261 } 262 263 Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) { 264 GlobalInitialization(); 265 } 266 267 void Slang::init(const std::string &Triple, const std::string &CPU, 268 const std::vector<std::string> &Features) { 269 if (mInitialized) 270 return; 271 272 createDiagnostic(); 273 llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine.getPtr()); 274 275 createTarget(Triple, CPU, Features); 276 createFileManager(); 277 createSourceManager(); 278 279 mInitialized = true; 280 } 281 282 clang::ModuleKey Slang::loadModule(clang::SourceLocation ImportLoc, 283 clang::IdentifierInfo &ModuleName, 284 clang::SourceLocation ModuleNameLoc) { 285 //FIXME: Don't we have to implement this? 286 slangAssert(0 && "Not implemented"); 287 return NULL; 288 } 289 290 bool Slang::setInputSource(llvm::StringRef InputFile, 291 const char *Text, 292 size_t TextLength) { 293 mInputFileName = InputFile.str(); 294 295 // Reset the ID tables if we are reusing the SourceManager 296 mSourceMgr->clearIDTables(); 297 298 // Load the source 299 llvm::MemoryBuffer *SB = 300 llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength); 301 mSourceMgr->createMainFileIDForMemBuffer(SB); 302 303 if (mSourceMgr->getMainFileID().isInvalid()) { 304 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; 305 return false; 306 } 307 return true; 308 } 309 310 bool Slang::setInputSource(llvm::StringRef InputFile) { 311 mInputFileName = InputFile.str(); 312 313 mSourceMgr->clearIDTables(); 314 315 const clang::FileEntry *File = mFileMgr->getFile(InputFile); 316 if (File) 317 mSourceMgr->createMainFileID(File); 318 319 if (mSourceMgr->getMainFileID().isInvalid()) { 320 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; 321 return false; 322 } 323 324 return true; 325 } 326 327 bool Slang::setOutput(const char *OutputFile) { 328 llvm::sys::Path OutputFilePath(OutputFile); 329 std::string Error; 330 llvm::tool_output_file *OS = NULL; 331 332 switch (mOT) { 333 case OT_Dependency: 334 case OT_Assembly: 335 case OT_LLVMAssembly: { 336 OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine.getPtr()); 337 break; 338 } 339 case OT_Nothing: { 340 break; 341 } 342 case OT_Object: 343 case OT_Bitcode: { 344 OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary, 345 &Error, mDiagEngine.getPtr()); 346 break; 347 } 348 default: { 349 llvm_unreachable("Unknown compiler output type"); 350 } 351 } 352 353 if (!Error.empty()) 354 return false; 355 356 mOS.reset(OS); 357 358 mOutputFileName = OutputFile; 359 360 return true; 361 } 362 363 bool Slang::setDepOutput(const char *OutputFile) { 364 llvm::sys::Path OutputFilePath(OutputFile); 365 std::string Error; 366 367 mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine.getPtr())); 368 if (!Error.empty() || (mDOS.get() == NULL)) 369 return false; 370 371 mDepOutputFileName = OutputFile; 372 373 return true; 374 } 375 376 int Slang::generateDepFile() { 377 if (mDiagEngine->hasErrorOccurred()) 378 return 1; 379 if (mDOS.get() == NULL) 380 return 1; 381 382 // Initialize options for generating dependency file 383 clang::DependencyOutputOptions DepOpts; 384 DepOpts.IncludeSystemHeaders = 1; 385 DepOpts.OutputFile = mDepOutputFileName; 386 DepOpts.Targets = mAdditionalDepTargets; 387 DepOpts.Targets.push_back(mDepTargetBCFileName); 388 for (std::vector<std::string>::const_iterator 389 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end(); 390 I != E; 391 I++) { 392 DepOpts.Targets.push_back(*I); 393 } 394 mGeneratedFileNames.clear(); 395 396 // Per-compilation needed initialization 397 createPreprocessor(); 398 AttachDependencyFileGen(*mPP.get(), DepOpts); 399 400 // Inform the diagnostic client we are processing a source file 401 mDiagClient->BeginSourceFile(LangOpts, mPP.get()); 402 403 // Go through the source file (no operations necessary) 404 clang::Token Tok; 405 mPP->EnterMainSourceFile(); 406 do { 407 mPP->Lex(Tok); 408 } while (Tok.isNot(clang::tok::eof)); 409 410 mPP->EndSourceFile(); 411 412 // Declare success if no error 413 if (!mDiagEngine->hasErrorOccurred()) 414 mDOS->keep(); 415 416 // Clean up after compilation 417 mPP.reset(); 418 mDOS.reset(); 419 420 return mDiagEngine->hasErrorOccurred() ? 1 : 0; 421 } 422 423 int Slang::compile() { 424 if (mDiagEngine->hasErrorOccurred()) 425 return 1; 426 if (mOS.get() == NULL) 427 return 1; 428 429 // Here is per-compilation needed initialization 430 createPreprocessor(); 431 createASTContext(); 432 433 mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT)); 434 435 // Inform the diagnostic client we are processing a source file 436 mDiagClient->BeginSourceFile(LangOpts, mPP.get()); 437 438 // The core of the slang compiler 439 ParseAST(*mPP, mBackend.get(), *mASTContext); 440 441 // Inform the diagnostic client we are done with previous source file 442 mDiagClient->EndSourceFile(); 443 444 // Declare success if no error 445 if (!mDiagEngine->hasErrorOccurred()) 446 mOS->keep(); 447 448 // The compilation ended, clear 449 mBackend.reset(); 450 mASTContext.reset(); 451 mPP.reset(); 452 mOS.reset(); 453 454 return mDiagEngine->hasErrorOccurred() ? 1 : 0; 455 } 456 457 void Slang::reset() { 458 llvm::errs() << mDiagClient->str(); 459 mDiagEngine->Reset(); 460 mDiagClient->reset(); 461 } 462 463 Slang::~Slang() { 464 llvm::llvm_shutdown(); 465 } 466 467 } // namespace slang 468