slang.cpp revision e3cb21bc8654181da174ac441acec7726b5a78ec
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 70namespace { 71 72struct 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 93namespace 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 104bool Slang::GlobalInitialized = false; 105 106// Language option (define the language feature for compiler such as C99) 107clang::LangOptions Slang::LangOpts; 108 109// Code generation option for the compiler 110clang::CodeGenOptions Slang::CodeGenOpts; 111 112// The named of metadata node that pragma resides (should be synced with 113// bcc.cpp) 114const llvm::StringRef Slang::PragmaMetadataName = "#pragma"; 115 116static inline llvm::tool_output_file * 117OpenOutputFile(const char *OutputFile, 118 unsigned Flags, 119 std::string* Error, 120 clang::DiagnosticsEngine *DiagEngine) { 121 slangAssert((OutputFile != NULL) && (Error != NULL) && 122 (DiagEngine != NULL) && "Invalid parameter!"); 123 124 if (SlangUtils::CreateDirectoryWithParents( 125 llvm::sys::path::parent_path(OutputFile), Error)) { 126 llvm::tool_output_file *F = 127 new llvm::tool_output_file(OutputFile, *Error, Flags); 128 if (F != NULL) 129 return F; 130 } 131 132 // Report error here. 133 DiagEngine->Report(clang::diag::err_fe_error_opening) 134 << OutputFile << *Error; 135 136 return NULL; 137} 138 139void Slang::GlobalInitialization() { 140 if (!GlobalInitialized) { 141 // We only support x86, x64 and ARM target 142 143 // For ARM 144 LLVMInitializeARMTargetInfo(); 145 LLVMInitializeARMTarget(); 146 LLVMInitializeARMAsmPrinter(); 147 148 // For x86 and x64 149 LLVMInitializeX86TargetInfo(); 150 LLVMInitializeX86Target(); 151 LLVMInitializeX86AsmPrinter(); 152 153 // Please refer to include/clang/Basic/LangOptions.h to setup 154 // the options. 155 LangOpts.RTTI = 0; // Turn off the RTTI information support 156 LangOpts.NeXTRuntime = 0; // Turn off the NeXT runtime uses 157 LangOpts.C99 = 1; 158 LangOpts.CharIsSigned = 1; // Signed char is our default. 159 160 CodeGenOpts.OptimizationLevel = 3; 161 162 GlobalInitialized = true; 163 } 164} 165 166void 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 174void Slang::createTarget(const std::string &Triple, const std::string &CPU, 175 const std::vector<std::string> &Features) { 176 if (!Triple.empty()) 177 mTargetOpts.Triple = Triple; 178 else 179 mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING; 180 181 if (!CPU.empty()) 182 mTargetOpts.CPU = CPU; 183 184 if (!Features.empty()) 185 mTargetOpts.Features = Features; 186 187 mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine, 188 mTargetOpts)); 189} 190 191void Slang::createFileManager() { 192 mFileSysOpt.reset(new clang::FileSystemOptions()); 193 mFileMgr.reset(new clang::FileManager(*mFileSysOpt)); 194} 195 196void Slang::createSourceManager() { 197 mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr)); 198} 199 200void Slang::createPreprocessor() { 201 // Default only search header file in current dir 202 clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(*mFileMgr, 203 *mDiagEngine); 204 205 mPP.reset(new clang::Preprocessor(*mDiagEngine, 206 LangOpts, 207 mTarget.get(), 208 *mSourceMgr, 209 *HeaderInfo, 210 *this, 211 NULL, 212 /* OwnsHeaderSearch = */true)); 213 // Initialize the preprocessor 214 mPragmas.clear(); 215 mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas)); 216 217 std::vector<clang::DirectoryLookup> SearchList; 218 for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) { 219 if (const clang::DirectoryEntry *DE = 220 mFileMgr->getDirectory(mIncludePaths[i])) { 221 SearchList.push_back(clang::DirectoryLookup(DE, 222 clang::SrcMgr::C_System, 223 false, 224 false)); 225 } 226 } 227 228 HeaderInfo->SetSearchPaths(SearchList, 229 /* angledDirIdx = */1, 230 /* systemDixIdx = */1, 231 /* noCurDirSearch = */false); 232 233 initPreprocessor(); 234} 235 236void Slang::createASTContext() { 237 mASTContext.reset(new clang::ASTContext(LangOpts, 238 *mSourceMgr, 239 mTarget.get(), 240 mPP->getIdentifierTable(), 241 mPP->getSelectorTable(), 242 mPP->getBuiltinInfo(), 243 /* size_reserve = */0)); 244 initASTContext(); 245} 246 247clang::ASTConsumer * 248Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts, 249 llvm::raw_ostream *OS, OutputType OT) { 250 return new Backend(mDiagEngine, CodeGenOpts, mTargetOpts, 251 &mPragmas, OS, OT); 252} 253 254Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) { 255 GlobalInitialization(); 256} 257 258void Slang::init(const std::string &Triple, const std::string &CPU, 259 const std::vector<std::string> &Features, 260 clang::DiagnosticsEngine *DiagEngine, 261 DiagnosticBuffer *DiagClient) { 262 if (mInitialized) 263 return; 264 265 mDiagEngine = DiagEngine; 266 mDiagClient = DiagClient; 267 mDiag.reset(new clang::Diagnostic(mDiagEngine)); 268 initDiagnostic(); 269 llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine); 270 271 createTarget(Triple, CPU, Features); 272 createFileManager(); 273 createSourceManager(); 274 275 mInitialized = true; 276} 277 278clang::Module *Slang::loadModule(clang::SourceLocation ImportLoc, 279 clang::ModuleIdPath Path, 280 clang::Module::NameVisibilityKind Visibility, 281 bool IsInclusionDirective) { 282 slangAssert(0 && "Not implemented"); 283 return NULL; 284} 285 286bool Slang::setInputSource(llvm::StringRef InputFile, 287 const char *Text, 288 size_t TextLength) { 289 mInputFileName = InputFile.str(); 290 291 // Reset the ID tables if we are reusing the SourceManager 292 mSourceMgr->clearIDTables(); 293 294 // Load the source 295 llvm::MemoryBuffer *SB = 296 llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength); 297 mSourceMgr->createMainFileIDForMemBuffer(SB); 298 299 if (mSourceMgr->getMainFileID().isInvalid()) { 300 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; 301 return false; 302 } 303 return true; 304} 305 306bool Slang::setInputSource(llvm::StringRef InputFile) { 307 mInputFileName = InputFile.str(); 308 309 mSourceMgr->clearIDTables(); 310 311 const clang::FileEntry *File = mFileMgr->getFile(InputFile); 312 if (File) 313 mSourceMgr->createMainFileID(File); 314 315 if (mSourceMgr->getMainFileID().isInvalid()) { 316 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; 317 return false; 318 } 319 320 return true; 321} 322 323bool Slang::setOutput(const char *OutputFile) { 324 llvm::sys::Path OutputFilePath(OutputFile); 325 std::string Error; 326 llvm::tool_output_file *OS = NULL; 327 328 switch (mOT) { 329 case OT_Dependency: 330 case OT_Assembly: 331 case OT_LLVMAssembly: { 332 OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine); 333 break; 334 } 335 case OT_Nothing: { 336 break; 337 } 338 case OT_Object: 339 case OT_Bitcode: { 340 OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary, 341 &Error, mDiagEngine); 342 break; 343 } 344 default: { 345 llvm_unreachable("Unknown compiler output type"); 346 } 347 } 348 349 if (!Error.empty()) 350 return false; 351 352 mOS.reset(OS); 353 354 mOutputFileName = OutputFile; 355 356 return true; 357} 358 359bool Slang::setDepOutput(const char *OutputFile) { 360 llvm::sys::Path OutputFilePath(OutputFile); 361 std::string Error; 362 363 mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine)); 364 if (!Error.empty() || (mDOS.get() == NULL)) 365 return false; 366 367 mDepOutputFileName = OutputFile; 368 369 return true; 370} 371 372int Slang::generateDepFile() { 373 if (mDiagEngine->hasErrorOccurred()) 374 return 1; 375 if (mDOS.get() == NULL) 376 return 1; 377 378 // Initialize options for generating dependency file 379 clang::DependencyOutputOptions DepOpts; 380 DepOpts.IncludeSystemHeaders = 1; 381 DepOpts.OutputFile = mDepOutputFileName; 382 DepOpts.Targets = mAdditionalDepTargets; 383 DepOpts.Targets.push_back(mDepTargetBCFileName); 384 for (std::vector<std::string>::const_iterator 385 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end(); 386 I != E; 387 I++) { 388 DepOpts.Targets.push_back(*I); 389 } 390 mGeneratedFileNames.clear(); 391 392 // Per-compilation needed initialization 393 createPreprocessor(); 394 AttachDependencyFileGen(*mPP.get(), DepOpts); 395 396 // Inform the diagnostic client we are processing a source file 397 mDiagClient->BeginSourceFile(LangOpts, mPP.get()); 398 399 // Go through the source file (no operations necessary) 400 clang::Token Tok; 401 mPP->EnterMainSourceFile(); 402 do { 403 mPP->Lex(Tok); 404 } while (Tok.isNot(clang::tok::eof)); 405 406 mPP->EndSourceFile(); 407 408 // Declare success if no error 409 if (!mDiagEngine->hasErrorOccurred()) 410 mDOS->keep(); 411 412 // Clean up after compilation 413 mPP.reset(); 414 mDOS.reset(); 415 416 return mDiagEngine->hasErrorOccurred() ? 1 : 0; 417} 418 419int Slang::compile() { 420 if (mDiagEngine->hasErrorOccurred()) 421 return 1; 422 if (mOS.get() == NULL) 423 return 1; 424 425 // Here is per-compilation needed initialization 426 createPreprocessor(); 427 createASTContext(); 428 429 mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT)); 430 431 // Inform the diagnostic client we are processing a source file 432 mDiagClient->BeginSourceFile(LangOpts, mPP.get()); 433 434 // The core of the slang compiler 435 ParseAST(*mPP, mBackend.get(), *mASTContext); 436 437 // Inform the diagnostic client we are done with previous source file 438 mDiagClient->EndSourceFile(); 439 440 // Declare success if no error 441 if (!mDiagEngine->hasErrorOccurred()) 442 mOS->keep(); 443 444 // The compilation ended, clear 445 mBackend.reset(); 446 mASTContext.reset(); 447 mPP.reset(); 448 mOS.reset(); 449 450 return mDiagEngine->hasErrorOccurred() ? 1 : 0; 451} 452 453void Slang::setDebugMetadataEmission(bool EmitDebug) { 454 CodeGenOpts.DebugInfo = EmitDebug; 455} 456 457void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) { 458 CodeGenOpts.OptimizationLevel = OptimizationLevel; 459} 460 461void Slang::reset() { 462 llvm::errs() << mDiagClient->str(); 463 mDiagEngine->Reset(); 464 mDiagClient->reset(); 465} 466 467Slang::~Slang() { 468 llvm::llvm_shutdown(); 469} 470 471} // namespace slang 472