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