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