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