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