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