slang.cpp revision 641558f02fe6ce0ee3ae5076eb366c25e2ad5903
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 106const std::string Slang::TargetDescription = 107 "e-" // little-endian 108 "p:32:32:32-" // 32-bit pointer 109 "i1:8:8-" 110 "i8:8:8-" 111 "i16:16:16-" 112 "i32:32:32-" 113 "i64:64:64-" 114 "f32:32:32-" 115 "f64:64:64-" 116 "v64:64:64-" // 64-bit vector (e.g. float2, int2, short4) 117 "v128:128:128-" 118 "a0:0:64-" 119 "n32"; // native CPU only support 32-bit integer width. 120 121// The named of metadata node that pragma resides (should be synced with 122// bcc.cpp) 123const llvm::StringRef Slang::PragmaMetadataName = "#pragma"; 124 125static inline llvm::tool_output_file *OpenOutputFile(const char *OutputFile, 126 unsigned Flags, 127 std::string* Error, 128 clang::Diagnostic* Diag) { 129 assert((OutputFile != NULL) && (Error != NULL) && (Diag != NULL) && 130 "Invalid parameter!"); 131 132 llvm::sys::Path OutputFilePath(OutputFile); 133 134 if (SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(), 135 Error)) { 136 llvm::tool_output_file *F = 137 new llvm::tool_output_file(OutputFile, *Error, Flags); 138 if (F != NULL) 139 return F; 140 } 141 142 // Report error here. 143 Diag->Report(clang::diag::err_fe_error_opening) << OutputFile << *Error; 144 145 return NULL; 146} 147 148void Slang::GlobalInitialization() { 149 if (!GlobalInitialized) { 150 // We only support x86, x64 and ARM target 151 152 // For ARM 153 LLVMInitializeARMTargetInfo(); 154 LLVMInitializeARMTarget(); 155 LLVMInitializeARMAsmPrinter(); 156 157 // For x86 and x64 158 LLVMInitializeX86TargetInfo(); 159 LLVMInitializeX86Target(); 160 LLVMInitializeX86AsmPrinter(); 161 162 // Please refer to include/clang/Basic/LangOptions.h to setup 163 // the options. 164 LangOpts.RTTI = 0; // Turn off the RTTI information support 165 LangOpts.NeXTRuntime = 0; // Turn off the NeXT runtime uses 166 LangOpts.Bool = 1; // Turn on 'bool', 'true', 'false' keywords 167 168 CodeGenOpts.OptimizationLevel = 3; /* -O3 */ 169 170 GlobalInitialized = true; 171 } 172 173 return; 174} 175 176void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) { 177 clang::Diagnostic* Diags = static_cast<clang::Diagnostic*>(UserData); 178 Diags->Report(clang::diag::err_fe_error_backend) << Message; 179 exit(1); 180} 181 182void Slang::createDiagnostic() { 183 mDiagnostics = 184 llvm::IntrusiveRefCntPtr<clang::Diagnostic>(new clang::Diagnostic()); 185 mDiagClient = new DiagnosticBuffer(); 186 // This takes the ownership of mDiagClient. 187 mDiagnostics->setClient(mDiagClient); 188 initDiagnostic(); 189 return; 190} 191 192void Slang::createTarget(const std::string &Triple, const std::string &CPU, 193 const std::vector<std::string> &Features) { 194 if (!Triple.empty()) 195 mTargetOpts.Triple = Triple; 196 else 197 mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING; 198 199 if (!CPU.empty()) 200 mTargetOpts.CPU = CPU; 201 202 if (!Features.empty()) 203 mTargetOpts.Features = Features; 204 205 mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagnostics, 206 mTargetOpts)); 207 208 return; 209} 210 211void Slang::createFileManager() { 212 mFileMgr.reset(new clang::FileManager()); 213} 214 215void Slang::createSourceManager() { 216 mSourceMgr.reset(new clang::SourceManager(*mDiagnostics)); 217 return; 218} 219 220void Slang::createPreprocessor() { 221 // Default only search header file in current dir 222 clang::HeaderSearch *HS = new clang::HeaderSearch(*mFileMgr); 223 224 mPP.reset(new clang::Preprocessor(*mDiagnostics, 225 LangOpts, 226 *mTarget, 227 *mSourceMgr, 228 *HS, 229 NULL, 230 /* OwnsHeaderSearch = */true)); 231 // Initialize the prepocessor 232 mPragmas.clear(); 233 mPP->AddPragmaHandler(new PragmaRecorder(mPragmas)); 234 235 std::vector<clang::DirectoryLookup> SearchList; 236 for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) { 237 if (const clang::DirectoryEntry *DE = 238 mFileMgr->getDirectory(mIncludePaths[i])) { 239 SearchList.push_back(clang::DirectoryLookup(DE, 240 clang::SrcMgr::C_System, 241 false, 242 false)); 243 } 244 } 245 246 HS->SetSearchPaths(SearchList, 1, false); 247 248 initPreprocessor(); 249 return; 250} 251 252void Slang::createASTContext() { 253 mASTContext.reset(new clang::ASTContext(LangOpts, 254 *mSourceMgr, 255 *mTarget, 256 mPP->getIdentifierTable(), 257 mPP->getSelectorTable(), 258 mPP->getBuiltinInfo(), 259 /* size_reserve = */0)); 260 initASTContext(); 261 return; 262} 263 264clang::ASTConsumer 265*Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts, 266 llvm::raw_ostream *OS, 267 OutputType OT) { 268 return new Backend(*mDiagnostics, 269 CodeGenOpts, 270 mTargetOpts, 271 mPragmas, 272 OS, 273 OT); 274} 275 276Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) { 277 GlobalInitialization(); 278 return; 279} 280 281void Slang::init(const std::string &Triple, const std::string &CPU, 282 const std::vector<std::string> &Features) { 283 if (mInitialized) 284 return; 285 286 createDiagnostic(); 287 llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.getPtr()); 288 289 createTarget(Triple, CPU, Features); 290 createFileManager(); 291 createSourceManager(); 292 293 mInitialized = true; 294 295 return; 296} 297 298bool Slang::setInputSource(llvm::StringRef InputFile, 299 const char *Text, 300 size_t TextLength) { 301 mInputFileName = InputFile.str(); 302 303 // Reset the ID tables if we are reusing the SourceManager 304 mSourceMgr->clearIDTables(); 305 306 // Load the source 307 llvm::MemoryBuffer *SB = 308 llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength); 309 mSourceMgr->createMainFileIDForMemBuffer(SB); 310 311 if (mSourceMgr->getMainFileID().isInvalid()) { 312 mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile; 313 return false; 314 } 315 return true; 316} 317 318bool Slang::setInputSource(llvm::StringRef InputFile) { 319 mInputFileName = InputFile.str(); 320 321 mSourceMgr->clearIDTables(); 322 323 const clang::FileEntry *File = mFileMgr->getFile(InputFile); 324 if (File) 325 mSourceMgr->createMainFileID(File); 326 327 if (mSourceMgr->getMainFileID().isInvalid()) { 328 mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile; 329 return false; 330 } 331 332 return true; 333} 334 335bool Slang::setOutput(const char *OutputFile) { 336 llvm::sys::Path OutputFilePath(OutputFile); 337 std::string Error; 338 llvm::tool_output_file *OS = NULL; 339 340 switch (mOT) { 341 case OT_Dependency: 342 case OT_Assembly: 343 case OT_LLVMAssembly: { 344 OS = OpenOutputFile(OutputFile, 0, &Error, mDiagnostics.getPtr()); 345 break; 346 } 347 case OT_Nothing: { 348 break; 349 } 350 case OT_Object: 351 case OT_Bitcode: { 352 OS = OpenOutputFile(OutputFile, 353 llvm::raw_fd_ostream::F_Binary, 354 &Error, 355 mDiagnostics.getPtr()); 356 break; 357 } 358 default: { 359 llvm_unreachable("Unknown compiler output type"); 360 } 361 } 362 363 if (!Error.empty()) 364 return false; 365 366 mOS.reset(OS); 367 368 mOutputFileName = OutputFile; 369 370 return true; 371} 372 373bool Slang::setDepOutput(const char *OutputFile) { 374 llvm::sys::Path OutputFilePath(OutputFile); 375 std::string Error; 376 377 mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagnostics.getPtr())); 378 if (!Error.empty() || (mDOS.get() == NULL)) 379 return false; 380 381 mDepOutputFileName = OutputFile; 382 383 return true; 384} 385 386int Slang::generateDepFile() { 387 if (mDiagnostics->getNumErrors() > 0) 388 return mDiagnostics->getNumErrors(); 389 if (mDOS.get() == NULL) 390 return 1; 391 392 // Initialize options for generating dependency file 393 clang::DependencyOutputOptions DepOpts; 394 DepOpts.IncludeSystemHeaders = 1; 395 DepOpts.OutputFile = mDepOutputFileName; 396 DepOpts.Targets = mAdditionalDepTargets; 397 DepOpts.Targets.push_back(mDepTargetBCFileName); 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.get(), 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