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