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