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