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