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