slang.cpp revision 3eb819ad8beec566a73b288204f9b75c2bb1d4e6
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/DiagnosticOptions.h" 29#include "clang/Basic/FileManager.h" 30#include "clang/Basic/FileSystemOptions.h" 31#include "clang/Basic/LangOptions.h" 32#include "clang/Basic/SourceManager.h" 33#include "clang/Basic/TargetInfo.h" 34#include "clang/Basic/TargetOptions.h" 35 36#include "clang/Frontend/CodeGenOptions.h" 37#include "clang/Frontend/DependencyOutputOptions.h" 38#include "clang/Frontend/FrontendDiagnostic.h" 39#include "clang/Frontend/FrontendOptions.h" 40#include "clang/Frontend/TextDiagnosticPrinter.h" 41#include "clang/Frontend/Utils.h" 42 43#include "clang/Lex/Preprocessor.h" 44#include "clang/Lex/PreprocessorOptions.h" 45#include "clang/Lex/HeaderSearch.h" 46#include "clang/Lex/HeaderSearchOptions.h" 47 48#include "clang/Parse/ParseAST.h" 49 50#include "llvm/ADT/IntrusiveRefCntPtr.h" 51 52#include "llvm/Bitcode/ReaderWriter.h" 53 54// More force linking 55#include "llvm/Linker/Linker.h" 56 57// Force linking all passes/vmcore stuffs to libslang.so 58#include "llvm/LinkAllIR.h" 59#include "llvm/LinkAllPasses.h" 60 61#include "llvm/Support/raw_ostream.h" 62#include "llvm/Support/MemoryBuffer.h" 63#include "llvm/Support/ErrorHandling.h" 64#include "llvm/Support/ManagedStatic.h" 65#include "llvm/Support/Path.h" 66#include "llvm/Support/TargetSelect.h" 67#include "llvm/Support/ToolOutputFile.h" 68 69#include "slang_assert.h" 70#include "slang_backend.h" 71 72namespace { 73 74static const char *kRSTriple32 = "armv7-none-linux-gnueabi"; 75static const char *kRSTriple64 = "aarch64-none-linux-gnueabi"; 76 77} // namespace 78 79namespace slang { 80 81bool Slang::GlobalInitialized = false; 82 83// Language option (define the language feature for compiler such as C99) 84clang::LangOptions Slang::LangOpts; 85 86// Code generation option for the compiler 87clang::CodeGenOptions Slang::CodeGenOpts; 88 89// The named of metadata node that pragma resides (should be synced with 90// bcc.cpp) 91const llvm::StringRef Slang::PragmaMetadataName = "#pragma"; 92 93static inline llvm::tool_output_file * 94OpenOutputFile(const char *OutputFile, 95 llvm::sys::fs::OpenFlags Flags, 96 std::error_code &EC, 97 clang::DiagnosticsEngine *DiagEngine) { 98 slangAssert((OutputFile != nullptr) && 99 (DiagEngine != nullptr) && "Invalid parameter!"); 100 101 EC = llvm::sys::fs::create_directories( 102 llvm::sys::path::parent_path(OutputFile)); 103 if (!EC) { 104 llvm::tool_output_file *F = 105 new llvm::tool_output_file(OutputFile, EC, Flags); 106 if (F != nullptr) 107 return F; 108 } 109 110 // Report error here. 111 DiagEngine->Report(clang::diag::err_fe_error_opening) 112 << OutputFile << EC.message(); 113 114 return nullptr; 115} 116 117void Slang::GlobalInitialization() { 118 if (!GlobalInitialized) { 119 // We only support x86, x64 and ARM target 120 121 // For ARM 122 LLVMInitializeARMTargetInfo(); 123 LLVMInitializeARMTarget(); 124 LLVMInitializeARMAsmPrinter(); 125 126 // For x86 and x64 127 LLVMInitializeX86TargetInfo(); 128 LLVMInitializeX86Target(); 129 LLVMInitializeX86AsmPrinter(); 130 131 // Please refer to include/clang/Basic/LangOptions.h to setup 132 // the options. 133 LangOpts.RTTI = 0; // Turn off the RTTI information support 134 LangOpts.C99 = 1; 135 LangOpts.Renderscript = 1; 136 LangOpts.LaxVectorConversions = 0; // Do not bitcast vectors! 137 LangOpts.CharIsSigned = 1; // Signed char is our default. 138 139 CodeGenOpts.OptimizationLevel = 3; 140 141 GlobalInitialized = true; 142 } 143} 144 145void Slang::LLVMErrorHandler(void *UserData, const std::string &Message, 146 bool GenCrashDialog) { 147 clang::DiagnosticsEngine* DiagEngine = 148 static_cast<clang::DiagnosticsEngine *>(UserData); 149 150 DiagEngine->Report(clang::diag::err_fe_error_backend) << Message; 151 exit(1); 152} 153 154void Slang::createTarget(uint32_t BitWidth) { 155 std::vector<std::string> features; 156 157 if (BitWidth == 64) { 158 mTargetOpts->Triple = kRSTriple64; 159 } else { 160 mTargetOpts->Triple = kRSTriple32; 161 // Treat long as a 64-bit type for our 32-bit RS code. 162 features.push_back("+long64"); 163 mTargetOpts->FeaturesAsWritten = features; 164 } 165 166 mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine, 167 mTargetOpts)); 168} 169 170void Slang::createFileManager() { 171 mFileSysOpt.reset(new clang::FileSystemOptions()); 172 mFileMgr.reset(new clang::FileManager(*mFileSysOpt)); 173} 174 175void Slang::createSourceManager() { 176 mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr)); 177} 178 179void Slang::createPreprocessor() { 180 // Default only search header file in current dir 181 llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> HSOpts = 182 new clang::HeaderSearchOptions(); 183 clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(HSOpts, 184 *mSourceMgr, 185 *mDiagEngine, 186 LangOpts, 187 mTarget.get()); 188 189 llvm::IntrusiveRefCntPtr<clang::PreprocessorOptions> PPOpts = 190 new clang::PreprocessorOptions(); 191 mPP.reset(new clang::Preprocessor(PPOpts, 192 *mDiagEngine, 193 LangOpts, 194 *mSourceMgr, 195 *HeaderInfo, 196 *this, 197 nullptr, 198 /* OwnsHeaderSearch = */true)); 199 // Initialize the preprocessor 200 mPP->Initialize(getTargetInfo()); 201 clang::FrontendOptions FEOpts; 202 clang::InitializePreprocessor(*mPP, *PPOpts, FEOpts); 203 204 mPragmas.clear(); 205 mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas)); 206 207 std::vector<clang::DirectoryLookup> SearchList; 208 for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) { 209 if (const clang::DirectoryEntry *DE = 210 mFileMgr->getDirectory(mIncludePaths[i])) { 211 SearchList.push_back(clang::DirectoryLookup(DE, 212 clang::SrcMgr::C_System, 213 false)); 214 } 215 } 216 217 HeaderInfo->SetSearchPaths(SearchList, 218 /* angledDirIdx = */1, 219 /* systemDixIdx = */1, 220 /* noCurDirSearch = */false); 221 222 initPreprocessor(); 223} 224 225void Slang::createASTContext() { 226 mASTContext.reset(new clang::ASTContext(LangOpts, 227 *mSourceMgr, 228 mPP->getIdentifierTable(), 229 mPP->getSelectorTable(), 230 mPP->getBuiltinInfo())); 231 mASTContext->InitBuiltinTypes(getTargetInfo()); 232 initASTContext(); 233} 234 235clang::ASTConsumer * 236Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts, 237 llvm::raw_ostream *OS, OutputType OT) { 238 return new Backend(mDiagEngine, CodeGenOpts, getTargetOptions(), 239 &mPragmas, OS, OT); 240} 241 242Slang::Slang() : mInitialized(false), mDiagClient(nullptr), 243 mTargetOpts(new clang::TargetOptions()), mOT(OT_Default) { 244 GlobalInitialization(); 245} 246 247void Slang::init(uint32_t BitWidth, clang::DiagnosticsEngine *DiagEngine, 248 DiagnosticBuffer *DiagClient) { 249 if (mInitialized) 250 return; 251 252 mDiagEngine = DiagEngine; 253 mDiagClient = DiagClient; 254 mDiag.reset(new clang::Diagnostic(mDiagEngine)); 255 initDiagnostic(); 256 llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine); 257 258 createTarget(BitWidth); 259 createFileManager(); 260 createSourceManager(); 261 262 mInitialized = true; 263} 264 265clang::ModuleLoadResult Slang::loadModule( 266 clang::SourceLocation ImportLoc, 267 clang::ModuleIdPath Path, 268 clang::Module::NameVisibilityKind Visibility, 269 bool IsInclusionDirective) { 270 slangAssert(0 && "Not implemented"); 271 return clang::ModuleLoadResult(); 272} 273 274bool Slang::setInputSource(llvm::StringRef InputFile, 275 const char *Text, 276 size_t TextLength) { 277 mInputFileName = InputFile.str(); 278 279 // Reset the ID tables if we are reusing the SourceManager 280 mSourceMgr->clearIDTables(); 281 282 // Load the source 283 std::unique_ptr<llvm::MemoryBuffer> SB = 284 llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength); 285 mSourceMgr->setMainFileID(mSourceMgr->createFileID(std::move(SB))); 286 287 if (mSourceMgr->getMainFileID().isInvalid()) { 288 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; 289 return false; 290 } 291 return true; 292} 293 294bool Slang::setInputSource(llvm::StringRef InputFile) { 295 mInputFileName = InputFile.str(); 296 297 mSourceMgr->clearIDTables(); 298 299 const clang::FileEntry *File = mFileMgr->getFile(InputFile); 300 if (File) { 301 mSourceMgr->setMainFileID(mSourceMgr->createFileID(File, 302 clang::SourceLocation(), clang::SrcMgr::C_User)); 303 } 304 305 if (mSourceMgr->getMainFileID().isInvalid()) { 306 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; 307 return false; 308 } 309 310 return true; 311} 312 313bool Slang::setOutput(const char *OutputFile) { 314 std::error_code EC; 315 llvm::tool_output_file *OS = nullptr; 316 317 switch (mOT) { 318 case OT_Dependency: 319 case OT_Assembly: 320 case OT_LLVMAssembly: { 321 OS = OpenOutputFile(OutputFile, llvm::sys::fs::F_Text, EC, mDiagEngine); 322 break; 323 } 324 case OT_Nothing: { 325 break; 326 } 327 case OT_Object: 328 case OT_Bitcode: { 329 OS = OpenOutputFile(OutputFile, llvm::sys::fs::F_None, EC, mDiagEngine); 330 break; 331 } 332 default: { 333 llvm_unreachable("Unknown compiler output type"); 334 } 335 } 336 337 if (EC) 338 return false; 339 340 mOS.reset(OS); 341 342 mOutputFileName = OutputFile; 343 344 return true; 345} 346 347bool Slang::setDepOutput(const char *OutputFile) { 348 std::error_code EC; 349 350 mDOS.reset( 351 OpenOutputFile(OutputFile, llvm::sys::fs::F_Text, EC, mDiagEngine)); 352 if (EC || (mDOS.get() == nullptr)) 353 return false; 354 355 mDepOutputFileName = OutputFile; 356 357 return true; 358} 359 360int Slang::generateDepFile() { 361 if (mDiagEngine->hasErrorOccurred()) 362 return 1; 363 if (mDOS.get() == nullptr) 364 return 1; 365 366 // Initialize options for generating dependency file 367 clang::DependencyOutputOptions DepOpts; 368 DepOpts.IncludeSystemHeaders = 1; 369 DepOpts.OutputFile = mDepOutputFileName; 370 DepOpts.Targets = mAdditionalDepTargets; 371 DepOpts.Targets.push_back(mDepTargetBCFileName); 372 for (std::vector<std::string>::const_iterator 373 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end(); 374 I != E; 375 I++) { 376 DepOpts.Targets.push_back(*I); 377 } 378 mGeneratedFileNames.clear(); 379 380 // Per-compilation needed initialization 381 createPreprocessor(); 382 clang::DependencyFileGenerator::CreateAndAttachToPreprocessor(*mPP.get(), DepOpts); 383 384 // Inform the diagnostic client we are processing a source file 385 mDiagClient->BeginSourceFile(LangOpts, mPP.get()); 386 387 // Go through the source file (no operations necessary) 388 clang::Token Tok; 389 mPP->EnterMainSourceFile(); 390 do { 391 mPP->Lex(Tok); 392 } while (Tok.isNot(clang::tok::eof)); 393 394 mPP->EndSourceFile(); 395 396 // Declare success if no error 397 if (!mDiagEngine->hasErrorOccurred()) 398 mDOS->keep(); 399 400 // Clean up after compilation 401 mPP.reset(); 402 mDOS.reset(); 403 404 return mDiagEngine->hasErrorOccurred() ? 1 : 0; 405} 406 407int Slang::compile() { 408 if (mDiagEngine->hasErrorOccurred()) 409 return 1; 410 if (mOS.get() == nullptr) 411 return 1; 412 413 // Here is per-compilation needed initialization 414 createPreprocessor(); 415 createASTContext(); 416 417 mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT)); 418 419 // Inform the diagnostic client we are processing a source file 420 mDiagClient->BeginSourceFile(LangOpts, mPP.get()); 421 422 // The core of the slang compiler 423 ParseAST(*mPP, mBackend.get(), *mASTContext); 424 425 // Inform the diagnostic client we are done with previous source file 426 mDiagClient->EndSourceFile(); 427 428 // Declare success if no error 429 if (!mDiagEngine->hasErrorOccurred()) 430 mOS->keep(); 431 432 // The compilation ended, clear 433 mBackend.reset(); 434 mOS.reset(); 435 436 return mDiagEngine->hasErrorOccurred() ? 1 : 0; 437} 438 439void Slang::setDebugMetadataEmission(bool EmitDebug) { 440 if (EmitDebug) 441 CodeGenOpts.setDebugInfo(clang::CodeGenOptions::FullDebugInfo); 442 else 443 CodeGenOpts.setDebugInfo(clang::CodeGenOptions::NoDebugInfo); 444} 445 446void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) { 447 CodeGenOpts.OptimizationLevel = OptimizationLevel; 448} 449 450void Slang::reset(bool SuppressWarnings) { 451 // Always print diagnostics if we had an error occur, but don't print 452 // warnings if we suppressed them (i.e. we are doing the 64-bit compile after 453 // an existing 32-bit compile). 454 // 455 // TODO: This should really be removing duplicate identical warnings between 456 // the 32-bit and 64-bit compiles, but that is a more substantial feature. 457 // Bug: 17052573 458 if (!SuppressWarnings || mDiagEngine->hasErrorOccurred()) { 459 llvm::errs() << mDiagClient->str(); 460 } 461 mDiagEngine->Reset(); 462 mDiagClient->reset(); 463} 464 465Slang::~Slang() { 466} 467 468} // namespace slang 469