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   slangAssert((OutputFile != NULL) && (Error != NULL) &&
    122               (DiagEngine != NULL) && "Invalid parameter!");
    123 
    124   if (SlangUtils::CreateDirectoryWithParents(
    125                         llvm::sys::path::parent_path(OutputFile), Error)) {
    126     llvm::tool_output_file *F =
    127           new llvm::tool_output_file(OutputFile, *Error, Flags);
    128     if (F != NULL)
    129       return F;
    130   }
    131 
    132   // Report error here.
    133   DiagEngine->Report(clang::diag::err_fe_error_opening)
    134     << OutputFile << *Error;
    135 
    136   return NULL;
    137 }
    138 
    139 void Slang::GlobalInitialization() {
    140   if (!GlobalInitialized) {
    141     // We only support x86, x64 and ARM target
    142 
    143     // For ARM
    144     LLVMInitializeARMTargetInfo();
    145     LLVMInitializeARMTarget();
    146     LLVMInitializeARMAsmPrinter();
    147 
    148     // For x86 and x64
    149     LLVMInitializeX86TargetInfo();
    150     LLVMInitializeX86Target();
    151     LLVMInitializeX86AsmPrinter();
    152 
    153     // Please refer to include/clang/Basic/LangOptions.h to setup
    154     // the options.
    155     LangOpts.RTTI = 0;  // Turn off the RTTI information support
    156     LangOpts.C99 = 1;
    157     LangOpts.Renderscript = 1;
    158     LangOpts.CharIsSigned = 1;  // Signed char is our default.
    159 
    160     CodeGenOpts.OptimizationLevel = 3;
    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::createTarget(const std::string &Triple, const std::string &CPU,
    175                          const std::vector<std::string> &Features) {
    176   if (!Triple.empty())
    177     mTargetOpts.Triple = Triple;
    178   else
    179     mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING;
    180 
    181   if (!CPU.empty())
    182     mTargetOpts.CPU = CPU;
    183 
    184   if (!Features.empty())
    185     mTargetOpts.Features = Features;
    186 
    187   mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
    188                                                     mTargetOpts));
    189 }
    190 
    191 void Slang::createFileManager() {
    192   mFileSysOpt.reset(new clang::FileSystemOptions());
    193   mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
    194 }
    195 
    196 void Slang::createSourceManager() {
    197   mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
    198 }
    199 
    200 void Slang::createPreprocessor() {
    201   // Default only search header file in current dir
    202   clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(*mFileMgr,
    203                                                             *mDiagEngine,
    204                                                             LangOpts,
    205                                                             mTarget.get());
    206 
    207   mPP.reset(new clang::Preprocessor(*mDiagEngine,
    208                                     LangOpts,
    209                                     mTarget.get(),
    210                                     *mSourceMgr,
    211                                     *HeaderInfo,
    212                                     *this,
    213                                     NULL,
    214                                     /* OwnsHeaderSearch = */true));
    215   // Initialize the preprocessor
    216   mPragmas.clear();
    217   mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
    218 
    219   std::vector<clang::DirectoryLookup> SearchList;
    220   for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
    221     if (const clang::DirectoryEntry *DE =
    222             mFileMgr->getDirectory(mIncludePaths[i])) {
    223       SearchList.push_back(clang::DirectoryLookup(DE,
    224                                                   clang::SrcMgr::C_System,
    225                                                   false,
    226                                                   false));
    227     }
    228   }
    229 
    230   HeaderInfo->SetSearchPaths(SearchList,
    231                              /* angledDirIdx = */1,
    232                              /* systemDixIdx = */1,
    233                              /* noCurDirSearch = */false);
    234 
    235   initPreprocessor();
    236 }
    237 
    238 void Slang::createASTContext() {
    239   mASTContext.reset(new clang::ASTContext(LangOpts,
    240                                           *mSourceMgr,
    241                                           mTarget.get(),
    242                                           mPP->getIdentifierTable(),
    243                                           mPP->getSelectorTable(),
    244                                           mPP->getBuiltinInfo(),
    245                                           /* size_reserve = */0));
    246   initASTContext();
    247 }
    248 
    249 clang::ASTConsumer *
    250 Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
    251                      llvm::raw_ostream *OS, OutputType OT) {
    252   return new Backend(mDiagEngine, CodeGenOpts, mTargetOpts,
    253                      &mPragmas, OS, OT);
    254 }
    255 
    256 Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
    257   GlobalInitialization();
    258 }
    259 
    260 void Slang::init(const std::string &Triple, const std::string &CPU,
    261                  const std::vector<std::string> &Features,
    262                  clang::DiagnosticsEngine *DiagEngine,
    263                  DiagnosticBuffer *DiagClient) {
    264   if (mInitialized)
    265     return;
    266 
    267   mDiagEngine = DiagEngine;
    268   mDiagClient = DiagClient;
    269   mDiag.reset(new clang::Diagnostic(mDiagEngine));
    270   initDiagnostic();
    271   llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
    272 
    273   createTarget(Triple, CPU, Features);
    274   createFileManager();
    275   createSourceManager();
    276 
    277   mInitialized = true;
    278 }
    279 
    280 clang::Module *Slang::loadModule(clang::SourceLocation ImportLoc,
    281                                  clang::ModuleIdPath Path,
    282                                  clang::Module::NameVisibilityKind Visibility,
    283                                  bool IsInclusionDirective) {
    284   slangAssert(0 && "Not implemented");
    285   return NULL;
    286 }
    287 
    288 bool Slang::setInputSource(llvm::StringRef InputFile,
    289                            const char *Text,
    290                            size_t TextLength) {
    291   mInputFileName = InputFile.str();
    292 
    293   // Reset the ID tables if we are reusing the SourceManager
    294   mSourceMgr->clearIDTables();
    295 
    296   // Load the source
    297   llvm::MemoryBuffer *SB =
    298       llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
    299   mSourceMgr->createMainFileIDForMemBuffer(SB);
    300 
    301   if (mSourceMgr->getMainFileID().isInvalid()) {
    302     mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
    303     return false;
    304   }
    305   return true;
    306 }
    307 
    308 bool Slang::setInputSource(llvm::StringRef InputFile) {
    309   mInputFileName = InputFile.str();
    310 
    311   mSourceMgr->clearIDTables();
    312 
    313   const clang::FileEntry *File = mFileMgr->getFile(InputFile);
    314   if (File)
    315     mSourceMgr->createMainFileID(File);
    316 
    317   if (mSourceMgr->getMainFileID().isInvalid()) {
    318     mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
    319     return false;
    320   }
    321 
    322   return true;
    323 }
    324 
    325 bool Slang::setOutput(const char *OutputFile) {
    326   llvm::sys::Path OutputFilePath(OutputFile);
    327   std::string Error;
    328   llvm::tool_output_file *OS = NULL;
    329 
    330   switch (mOT) {
    331     case OT_Dependency:
    332     case OT_Assembly:
    333     case OT_LLVMAssembly: {
    334       OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine);
    335       break;
    336     }
    337     case OT_Nothing: {
    338       break;
    339     }
    340     case OT_Object:
    341     case OT_Bitcode: {
    342       OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary,
    343                           &Error, mDiagEngine);
    344       break;
    345     }
    346     default: {
    347       llvm_unreachable("Unknown compiler output type");
    348     }
    349   }
    350 
    351   if (!Error.empty())
    352     return false;
    353 
    354   mOS.reset(OS);
    355 
    356   mOutputFileName = OutputFile;
    357 
    358   return true;
    359 }
    360 
    361 bool Slang::setDepOutput(const char *OutputFile) {
    362   llvm::sys::Path OutputFilePath(OutputFile);
    363   std::string Error;
    364 
    365   mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine));
    366   if (!Error.empty() || (mDOS.get() == NULL))
    367     return false;
    368 
    369   mDepOutputFileName = OutputFile;
    370 
    371   return true;
    372 }
    373 
    374 int Slang::generateDepFile() {
    375   if (mDiagEngine->hasErrorOccurred())
    376     return 1;
    377   if (mDOS.get() == NULL)
    378     return 1;
    379 
    380   // Initialize options for generating dependency file
    381   clang::DependencyOutputOptions DepOpts;
    382   DepOpts.IncludeSystemHeaders = 1;
    383   DepOpts.OutputFile = mDepOutputFileName;
    384   DepOpts.Targets = mAdditionalDepTargets;
    385   DepOpts.Targets.push_back(mDepTargetBCFileName);
    386   for (std::vector<std::string>::const_iterator
    387            I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
    388        I != E;
    389        I++) {
    390     DepOpts.Targets.push_back(*I);
    391   }
    392   mGeneratedFileNames.clear();
    393 
    394   // Per-compilation needed initialization
    395   createPreprocessor();
    396   AttachDependencyFileGen(*mPP.get(), DepOpts);
    397 
    398   // Inform the diagnostic client we are processing a source file
    399   mDiagClient->BeginSourceFile(LangOpts, mPP.get());
    400 
    401   // Go through the source file (no operations necessary)
    402   clang::Token Tok;
    403   mPP->EnterMainSourceFile();
    404   do {
    405     mPP->Lex(Tok);
    406   } while (Tok.isNot(clang::tok::eof));
    407 
    408   mPP->EndSourceFile();
    409 
    410   // Declare success if no error
    411   if (!mDiagEngine->hasErrorOccurred())
    412     mDOS->keep();
    413 
    414   // Clean up after compilation
    415   mPP.reset();
    416   mDOS.reset();
    417 
    418   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
    419 }
    420 
    421 int Slang::compile() {
    422   if (mDiagEngine->hasErrorOccurred())
    423     return 1;
    424   if (mOS.get() == NULL)
    425     return 1;
    426 
    427   // Here is per-compilation needed initialization
    428   createPreprocessor();
    429   createASTContext();
    430 
    431   mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT));
    432 
    433   // Inform the diagnostic client we are processing a source file
    434   mDiagClient->BeginSourceFile(LangOpts, mPP.get());
    435 
    436   // The core of the slang compiler
    437   ParseAST(*mPP, mBackend.get(), *mASTContext);
    438 
    439   // Inform the diagnostic client we are done with previous source file
    440   mDiagClient->EndSourceFile();
    441 
    442   // Declare success if no error
    443   if (!mDiagEngine->hasErrorOccurred())
    444     mOS->keep();
    445 
    446   // The compilation ended, clear
    447   mBackend.reset();
    448   mASTContext.reset();
    449   mPP.reset();
    450   mOS.reset();
    451 
    452   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
    453 }
    454 
    455 void Slang::setDebugMetadataEmission(bool EmitDebug) {
    456   if (EmitDebug)
    457     CodeGenOpts.DebugInfo = clang::CodeGenOptions::FullDebugInfo;
    458   else
    459     CodeGenOpts.DebugInfo = clang::CodeGenOptions::NoDebugInfo;
    460 }
    461 
    462 void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
    463   CodeGenOpts.OptimizationLevel = OptimizationLevel;
    464 }
    465 
    466 void Slang::reset() {
    467   llvm::errs() << mDiagClient->str();
    468   mDiagEngine->Reset();
    469   mDiagClient->reset();
    470 }
    471 
    472 Slang::~Slang() {
    473   llvm::llvm_shutdown();
    474 }
    475 
    476 }  // namespace slang
    477