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