| /* |
| * Copyright 2010, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "slang.h" |
| |
| #include <stdlib.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "clang/AST/ASTConsumer.h" |
| #include "clang/AST/ASTContext.h" |
| |
| #include "clang/Basic/DiagnosticIDs.h" |
| #include "clang/Basic/FileManager.h" |
| #include "clang/Basic/FileSystemOptions.h" |
| #include "clang/Basic/LangOptions.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/Basic/TargetOptions.h" |
| |
| #include "clang/Frontend/CodeGenOptions.h" |
| #include "clang/Frontend/DiagnosticOptions.h" |
| #include "clang/Frontend/DependencyOutputOptions.h" |
| #include "clang/Frontend/FrontendDiagnostic.h" |
| #include "clang/Frontend/TextDiagnosticPrinter.h" |
| #include "clang/Frontend/Utils.h" |
| |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Lex/HeaderSearch.h" |
| |
| #include "clang/Parse/ParseAST.h" |
| |
| #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| |
| #include "llvm/Bitcode/ReaderWriter.h" |
| |
| // More force linking |
| #include "llvm/Linker.h" |
| |
| // Force linking all passes/vmcore stuffs to libslang.so |
| #include "llvm/LinkAllPasses.h" |
| #include "llvm/LinkAllVMCore.h" |
| |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/ManagedStatic.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| |
| #include "slang_assert.h" |
| #include "slang_backend.h" |
| #include "slang_utils.h" |
| |
| namespace { |
| |
| struct ForceSlangLinking { |
| ForceSlangLinking() { |
| // We must reference the functions in such a way that compilers will not |
| // delete it all as dead code, even with whole program optimization, |
| // yet is effectively a NO-OP. As the compiler isn't smart enough |
| // to know that getenv() never returns -1, this will do the job. |
| if (std::getenv("bar") != reinterpret_cast<char*>(-1)) |
| return; |
| |
| // llvm-rs-link needs following functions existing in libslang. |
| llvm::ParseBitcodeFile(NULL, llvm::getGlobalContext(), NULL); |
| llvm::Linker::LinkModules(NULL, NULL, 0, NULL); |
| |
| // llvm-rs-cc need this. |
| new clang::TextDiagnosticPrinter(llvm::errs(), |
| clang::DiagnosticOptions()); |
| } |
| } ForceSlangLinking; |
| |
| } // namespace |
| |
| namespace slang { |
| |
| #if defined(__arm__) |
| # define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi" |
| #elif defined(__x86_64__) |
| # define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux" |
| #else |
| // let's use x86 as default target |
| # define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux" |
| #endif |
| |
| bool Slang::GlobalInitialized = false; |
| |
| // Language option (define the language feature for compiler such as C99) |
| clang::LangOptions Slang::LangOpts; |
| |
| // Code generation option for the compiler |
| clang::CodeGenOptions Slang::CodeGenOpts; |
| |
| // The named of metadata node that pragma resides (should be synced with |
| // bcc.cpp) |
| const llvm::StringRef Slang::PragmaMetadataName = "#pragma"; |
| |
| static inline llvm::tool_output_file * |
| OpenOutputFile(const char *OutputFile, |
| unsigned Flags, |
| std::string* Error, |
| clang::DiagnosticsEngine *DiagEngine) |
| { |
| slangAssert((OutputFile != NULL) && (Error != NULL) && |
| (DiagEngine != NULL) && "Invalid parameter!"); |
| |
| if (SlangUtils::CreateDirectoryWithParents( |
| llvm::sys::path::parent_path(OutputFile), Error)) { |
| llvm::tool_output_file *F = |
| new llvm::tool_output_file(OutputFile, *Error, Flags); |
| if (F != NULL) |
| return F; |
| } |
| |
| // Report error here. |
| DiagEngine->Report(clang::diag::err_fe_error_opening) |
| << OutputFile << *Error; |
| |
| return NULL; |
| } |
| |
| void Slang::GlobalInitialization() { |
| if (!GlobalInitialized) { |
| // We only support x86, x64 and ARM target |
| |
| // For ARM |
| LLVMInitializeARMTargetInfo(); |
| LLVMInitializeARMTarget(); |
| LLVMInitializeARMAsmPrinter(); |
| |
| // For x86 and x64 |
| LLVMInitializeX86TargetInfo(); |
| LLVMInitializeX86Target(); |
| LLVMInitializeX86AsmPrinter(); |
| |
| // Please refer to include/clang/Basic/LangOptions.h to setup |
| // the options. |
| LangOpts.RTTI = 0; // Turn off the RTTI information support |
| LangOpts.NeXTRuntime = 0; // Turn off the NeXT runtime uses |
| LangOpts.C99 = 1; |
| |
| CodeGenOpts.OptimizationLevel = 3; /* -O3 */ |
| |
| GlobalInitialized = true; |
| } |
| } |
| |
| void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) { |
| clang::DiagnosticsEngine* DiagEngine = |
| static_cast<clang::DiagnosticsEngine *>(UserData); |
| |
| DiagEngine->Report(clang::diag::err_fe_error_backend) << Message; |
| exit(1); |
| } |
| |
| void Slang::createDiagnostic() { |
| mDiagClient = new DiagnosticBuffer(); |
| |
| mDiagIDs = new clang::DiagnosticIDs(); |
| mDiagEngine = new clang::DiagnosticsEngine(mDiagIDs, mDiagClient, true); |
| mDiag.reset(new clang::Diagnostic(mDiagEngine.getPtr())); |
| |
| initDiagnostic(); |
| } |
| |
| void Slang::createTarget(const std::string &Triple, const std::string &CPU, |
| const std::vector<std::string> &Features) { |
| if (!Triple.empty()) |
| mTargetOpts.Triple = Triple; |
| else |
| mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING; |
| |
| if (!CPU.empty()) |
| mTargetOpts.CPU = CPU; |
| |
| if (!Features.empty()) |
| mTargetOpts.Features = Features; |
| |
| mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine, |
| mTargetOpts)); |
| } |
| |
| void Slang::createFileManager() { |
| mFileSysOpt.reset(new clang::FileSystemOptions()); |
| mFileMgr.reset(new clang::FileManager(*mFileSysOpt)); |
| } |
| |
| void Slang::createSourceManager() { |
| mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr)); |
| } |
| |
| void Slang::createPreprocessor() { |
| // Default only search header file in current dir |
| clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(*mFileMgr, |
| *mDiagEngine); |
| |
| mPP.reset(new clang::Preprocessor(*mDiagEngine, |
| LangOpts, |
| mTarget.get(), |
| *mSourceMgr, |
| *HeaderInfo, |
| *this, |
| NULL, |
| /* OwnsHeaderSearch = */true)); |
| // Initialize the preprocessor |
| mPragmas.clear(); |
| mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas)); |
| |
| std::vector<clang::DirectoryLookup> SearchList; |
| for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) { |
| if (const clang::DirectoryEntry *DE = |
| mFileMgr->getDirectory(mIncludePaths[i])) { |
| SearchList.push_back(clang::DirectoryLookup(DE, |
| clang::SrcMgr::C_System, |
| false, |
| false)); |
| } |
| } |
| |
| HeaderInfo->SetSearchPaths(SearchList, |
| /* angledDirIdx = */1, |
| /* systemDixIdx = */1, |
| /* noCurDirSearch = */false); |
| |
| initPreprocessor(); |
| } |
| |
| void Slang::createASTContext() { |
| mASTContext.reset(new clang::ASTContext(LangOpts, |
| *mSourceMgr, |
| mTarget.get(), |
| mPP->getIdentifierTable(), |
| mPP->getSelectorTable(), |
| mPP->getBuiltinInfo(), |
| /* size_reserve = */0)); |
| initASTContext(); |
| } |
| |
| clang::ASTConsumer * |
| Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts, |
| llvm::raw_ostream *OS, OutputType OT) { |
| return new Backend(mDiagEngine.getPtr(), CodeGenOpts, mTargetOpts, |
| &mPragmas, OS, OT); |
| } |
| |
| Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) { |
| GlobalInitialization(); |
| } |
| |
| void Slang::init(const std::string &Triple, const std::string &CPU, |
| const std::vector<std::string> &Features) { |
| if (mInitialized) |
| return; |
| |
| createDiagnostic(); |
| llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine.getPtr()); |
| |
| createTarget(Triple, CPU, Features); |
| createFileManager(); |
| createSourceManager(); |
| |
| mInitialized = true; |
| } |
| |
| clang::ModuleKey Slang::loadModule(clang::SourceLocation ImportLoc, |
| clang::IdentifierInfo &ModuleName, |
| clang::SourceLocation ModuleNameLoc) { |
| //FIXME: Don't we have to implement this? |
| slangAssert(0 && "Not implemented"); |
| return NULL; |
| } |
| |
| bool Slang::setInputSource(llvm::StringRef InputFile, |
| const char *Text, |
| size_t TextLength) { |
| mInputFileName = InputFile.str(); |
| |
| // Reset the ID tables if we are reusing the SourceManager |
| mSourceMgr->clearIDTables(); |
| |
| // Load the source |
| llvm::MemoryBuffer *SB = |
| llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength); |
| mSourceMgr->createMainFileIDForMemBuffer(SB); |
| |
| if (mSourceMgr->getMainFileID().isInvalid()) { |
| mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; |
| return false; |
| } |
| return true; |
| } |
| |
| bool Slang::setInputSource(llvm::StringRef InputFile) { |
| mInputFileName = InputFile.str(); |
| |
| mSourceMgr->clearIDTables(); |
| |
| const clang::FileEntry *File = mFileMgr->getFile(InputFile); |
| if (File) |
| mSourceMgr->createMainFileID(File); |
| |
| if (mSourceMgr->getMainFileID().isInvalid()) { |
| mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool Slang::setOutput(const char *OutputFile) { |
| llvm::sys::Path OutputFilePath(OutputFile); |
| std::string Error; |
| llvm::tool_output_file *OS = NULL; |
| |
| switch (mOT) { |
| case OT_Dependency: |
| case OT_Assembly: |
| case OT_LLVMAssembly: { |
| OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine.getPtr()); |
| break; |
| } |
| case OT_Nothing: { |
| break; |
| } |
| case OT_Object: |
| case OT_Bitcode: { |
| OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary, |
| &Error, mDiagEngine.getPtr()); |
| break; |
| } |
| default: { |
| llvm_unreachable("Unknown compiler output type"); |
| } |
| } |
| |
| if (!Error.empty()) |
| return false; |
| |
| mOS.reset(OS); |
| |
| mOutputFileName = OutputFile; |
| |
| return true; |
| } |
| |
| bool Slang::setDepOutput(const char *OutputFile) { |
| llvm::sys::Path OutputFilePath(OutputFile); |
| std::string Error; |
| |
| mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine.getPtr())); |
| if (!Error.empty() || (mDOS.get() == NULL)) |
| return false; |
| |
| mDepOutputFileName = OutputFile; |
| |
| return true; |
| } |
| |
| int Slang::generateDepFile() { |
| if (mDiagEngine->hasErrorOccurred()) |
| return 1; |
| if (mDOS.get() == NULL) |
| return 1; |
| |
| // Initialize options for generating dependency file |
| clang::DependencyOutputOptions DepOpts; |
| DepOpts.IncludeSystemHeaders = 1; |
| DepOpts.OutputFile = mDepOutputFileName; |
| DepOpts.Targets = mAdditionalDepTargets; |
| DepOpts.Targets.push_back(mDepTargetBCFileName); |
| for (std::vector<std::string>::const_iterator |
| I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end(); |
| I != E; |
| I++) { |
| DepOpts.Targets.push_back(*I); |
| } |
| mGeneratedFileNames.clear(); |
| |
| // Per-compilation needed initialization |
| createPreprocessor(); |
| AttachDependencyFileGen(*mPP.get(), DepOpts); |
| |
| // Inform the diagnostic client we are processing a source file |
| mDiagClient->BeginSourceFile(LangOpts, mPP.get()); |
| |
| // Go through the source file (no operations necessary) |
| clang::Token Tok; |
| mPP->EnterMainSourceFile(); |
| do { |
| mPP->Lex(Tok); |
| } while (Tok.isNot(clang::tok::eof)); |
| |
| mPP->EndSourceFile(); |
| |
| // Declare success if no error |
| if (!mDiagEngine->hasErrorOccurred()) |
| mDOS->keep(); |
| |
| // Clean up after compilation |
| mPP.reset(); |
| mDOS.reset(); |
| |
| return mDiagEngine->hasErrorOccurred() ? 1 : 0; |
| } |
| |
| int Slang::compile() { |
| if (mDiagEngine->hasErrorOccurred()) |
| return 1; |
| if (mOS.get() == NULL) |
| return 1; |
| |
| // Here is per-compilation needed initialization |
| createPreprocessor(); |
| createASTContext(); |
| |
| mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT)); |
| |
| // Inform the diagnostic client we are processing a source file |
| mDiagClient->BeginSourceFile(LangOpts, mPP.get()); |
| |
| // The core of the slang compiler |
| ParseAST(*mPP, mBackend.get(), *mASTContext); |
| |
| // Inform the diagnostic client we are done with previous source file |
| mDiagClient->EndSourceFile(); |
| |
| // Declare success if no error |
| if (!mDiagEngine->hasErrorOccurred()) |
| mOS->keep(); |
| |
| // The compilation ended, clear |
| mBackend.reset(); |
| mASTContext.reset(); |
| mPP.reset(); |
| mOS.reset(); |
| |
| return mDiagEngine->hasErrorOccurred() ? 1 : 0; |
| } |
| |
| void Slang::reset() { |
| llvm::errs() << mDiagClient->str(); |
| mDiagEngine->Reset(); |
| mDiagClient->reset(); |
| } |
| |
| Slang::~Slang() { |
| llvm::llvm_shutdown(); |
| } |
| |
| } // namespace slang |