C++程序  |  325行  |  8.96 KB

#include "Compiler.h"

#include <cassert>
#include <cstdlib>
#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/LLVMContext.h"

#include "llvm/ADT/IntrusiveRefCntPtr.h"

#include "llvm/Bitcode/ReaderWriter.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/ToolOutputFile.h"
#include "llvm/Support/Path.h"

#include "llvm/Target/TargetSelect.h"

#include "Backend.h"

namespace ndkpc {

static inline llvm::tool_output_file *openOutputFile(const char *OutputFile,
                                                     unsigned Flags,
                                                     std::string* Error,
                                                     clang::Diagnostic* Diag) {
  assert((OutputFile != NULL) && (Error != NULL) && (Diag != NULL) &&
              "Invalid parameter!");

  llvm::tool_output_file *F =
        new llvm::tool_output_file(OutputFile, *Error, Flags);
  if (F != NULL)
    return F;

  // Report error here.
  Diag->Report(clang::diag::err_fe_error_opening) << OutputFile << *Error;

  return NULL;
}

void Compiler::LLVMErrorHandler(void *UserData, const std::string &Message) {
  clang::Diagnostic* Diags = static_cast<clang::Diagnostic*>(UserData);
  Diags->Report(clang::diag::err_fe_error_backend) << Message;
  exit(1);
}

void Compiler::createDiagnostic() {
  mpDiagClient = new clang::TextDiagnosticPrinter(llvm::errs(),
                                                 clang::DiagnosticOptions());
  mDiagIDs = new clang::DiagnosticIDs();
  mDiagnostics = new clang::Diagnostic(mDiagIDs, mpDiagClient, true);
  initDiagnostic();
  return;
}

void Compiler::createTarget(const std::string &Triple, const std::string &CPU,
                         const std::vector<std::string> &Features) {
  if (!Triple.empty())
    mTargetOpts.Triple = Triple;
  else
    mTargetOpts.Triple = llvm::Triple::normalize(DEFAULT_TARGET_TRIPLE_STRING);

  if (!CPU.empty())
    mTargetOpts.CPU = CPU;

  if (!Features.empty())
    mTargetOpts.Features = Features;

  mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagnostics,
                                                    mTargetOpts));

  return;
}

void Compiler::createFileManager() {
  mFileSysOpt.reset(new clang::FileSystemOptions());
  mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
}

void Compiler::createSourceManager() {
  mSourceMgr.reset(new clang::SourceManager(*mDiagnostics, *mFileMgr));
  return;
}

void Compiler::createPreprocessor() {
  clang::HeaderSearch *HS = new clang::HeaderSearch(*mFileMgr);

  mPP.reset(new clang::Preprocessor(*mDiagnostics,
                                    mLangOpts,
                                    *mTarget,
                                    *mSourceMgr,
                                    *HS,
                                    NULL, /* IILookup */
                                    /* OwnsHeaderSearch = */true));

  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, /* isUser */
                                                  false /* isFramework */));
    }
  }

  HS->SetSearchPaths(SearchList, 0/* angledDirIdx FIXME CHECK */, 0/* systemDirIdx */, false/* noCurDirSearch */);

  initPreprocessor();
  return;
}

void Compiler::createASTContext() {
  mASTContext.reset(new clang::ASTContext(mLangOpts,
                                          *mSourceMgr,
                                          *mTarget,
                                          mPP->getIdentifierTable(),
                                          mPP->getSelectorTable(),
                                          mPP->getBuiltinInfo(),
                                          /* size_reserve = */0));
  initASTContext();
  return;
}

clang::ASTConsumer
*Compiler::createBackend(const clang::CodeGenOptions& CodeGenOpts,
                      llvm::raw_ostream *OS,
                      OutputType OT) {
  return new Backend(CodeGenOpts,
                     mTargetOpts,
                     mDiagnostics.getPtr(),
                     OS,
                     OT);
}

Compiler::Compiler() : mInitialized(false), mpDiagClient(NULL), mOT(OT_Default) {
}

void Compiler::injectPreDefined() {
  typedef std::map<std::string, std::string> SymbolMapTy;
  for (SymbolMapTy::iterator
          it = mPreDefinedSymbolMap.begin(), et = mPreDefinedSymbolMap.end();
       it != et; ++it) {
    std::string Str = "#define "+it->first+" "+it->second+"\n";
    mPP->setPredefines(Str);
  }
}

void Compiler::init(const std::string &Triple, const std::string &CPU,
                    const std::vector<std::string> &Features, bool isCXX) {
  mLangOpts.RTTI = 0;  // Turn off the RTTI information support
  mLangOpts.NeXTRuntime = 0;   // Turn off the NeXT runtime uses
  mLangOpts.C99 = 1;
  if (isCXX) {
    mLangOpts.CPlusPlus = 1;
  }

  mCodeGenOpts.OptimizationLevel = 3;  /* -O3 */

  createDiagnostic();
  llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.getPtr());

  createTarget(Triple, CPU, Features);
  createFileManager();
  createSourceManager();

  mInitialized = true;

  return;
}

bool Compiler::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()) {
    mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile;
    return false;
  }
  return true;
}

bool Compiler::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()) {
    mDiagnostics->Report(clang::diag::err_fe_error_reading) << InputFile;
    return false;
  }

  return true;
}

bool Compiler::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, mDiagnostics.getPtr());
      break;
    }
    case OT_Nothing: {
      break;
    }
    case OT_Object:
    case OT_Bitcode: {
      OS = openOutputFile(OutputFile,
                          llvm::raw_fd_ostream::F_Binary,
                          &Error,
                          mDiagnostics.getPtr());
      break;
    }
    default: {
      llvm_unreachable("Unknown compiler output type");
    }
  }

  if (!Error.empty())
    return false;

  mOS.reset(OS);

  mOutputFileName = OutputFile;

  return true;
}

int Compiler::compile() {
  if (mDiagnostics->hasErrorOccurred())
    return 1;
  if (mOS.get() == NULL)
    return 1;

  // Here is per-compilation needed initialization
  createPreprocessor();
  createASTContext();

  mBackend.reset(createBackend(mCodeGenOpts, &mOS->os(), mOT));

  // Inform the diagnostic client we are processing a source file
  mpDiagClient->BeginSourceFile(mLangOpts, mPP.get());

  if (mLangOpts.CPlusPlus == 1) {
    mPP->setPredefines("#define __cplusplus\n");
  }

  this->injectPreDefined();

  // The core of the slang compiler
  ParseAST(*mPP, mBackend.get(), *mASTContext);

  // Inform the diagnostic client we are done with previous source file
  mpDiagClient->EndSourceFile();

  // Declare success if no error
  if (!mDiagnostics->hasErrorOccurred())
    mOS->keep();

  // The compilation ended, clear
  mBackend.reset();
  mASTContext.reset();
  mPP.reset();
  mOS.reset();

  return mDiagnostics->hasErrorOccurred() ? 1 : 0;
}

void Compiler::reset() {
  mDiagnostics->Reset();
  return;
}

Compiler::~Compiler() {
  llvm::llvm_shutdown();
  return;
}

}