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