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                unsigned 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.CharIsSigned = 1;  // Signed char is our default.
    161 
    162     CodeGenOpts.OptimizationLevel = 3;
    163 
    164     GlobalInitialized = true;
    165   }
    166 }
    167 
    168 void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
    169   clang::DiagnosticsEngine* DiagEngine =
    170     static_cast<clang::DiagnosticsEngine *>(UserData);
    171 
    172   DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
    173   exit(1);
    174 }
    175 
    176 void Slang::createTarget(const std::string &Triple, const std::string &CPU,
    177                          const std::vector<std::string> &Features) {
    178   if (!Triple.empty())
    179     mTargetOpts->Triple = Triple;
    180   else
    181     mTargetOpts->Triple = DEFAULT_TARGET_TRIPLE_STRING;
    182 
    183   if (!CPU.empty())
    184     mTargetOpts->CPU = CPU;
    185 
    186   if (!Features.empty())
    187     mTargetOpts->FeaturesAsWritten = Features;
    188 
    189   mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
    190                                                     mTargetOpts.getPtr()));
    191 }
    192 
    193 void Slang::createFileManager() {
    194   mFileSysOpt.reset(new clang::FileSystemOptions());
    195   mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
    196 }
    197 
    198 void Slang::createSourceManager() {
    199   mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
    200 }
    201 
    202 void Slang::createPreprocessor() {
    203   // Default only search header file in current dir
    204   llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> HSOpts =
    205       new clang::HeaderSearchOptions();
    206   clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(HSOpts,
    207                                                             *mFileMgr,
    208                                                             *mDiagEngine,
    209                                                             LangOpts,
    210                                                             mTarget.get());
    211 
    212   llvm::IntrusiveRefCntPtr<clang::PreprocessorOptions> PPOpts =
    213       new clang::PreprocessorOptions();
    214   mPP.reset(new clang::Preprocessor(PPOpts,
    215                                     *mDiagEngine,
    216                                     LangOpts,
    217                                     mTarget.get(),
    218                                     *mSourceMgr,
    219                                     *HeaderInfo,
    220                                     *this,
    221                                     NULL,
    222                                     /* OwnsHeaderSearch = */true));
    223   // Initialize the preprocessor
    224   mPragmas.clear();
    225   mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
    226 
    227   std::vector<clang::DirectoryLookup> SearchList;
    228   for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
    229     if (const clang::DirectoryEntry *DE =
    230             mFileMgr->getDirectory(mIncludePaths[i])) {
    231       SearchList.push_back(clang::DirectoryLookup(DE,
    232                                                   clang::SrcMgr::C_System,
    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, CodeGenOpts, getTargetOptions(),
    260                      &mPragmas, OS, OT);
    261 }
    262 
    263 Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
    264   mTargetOpts = new clang::TargetOptions();
    265   GlobalInitialization();
    266 }
    267 
    268 void Slang::init(const std::string &Triple, const std::string &CPU,
    269                  const std::vector<std::string> &Features,
    270                  clang::DiagnosticsEngine *DiagEngine,
    271                  DiagnosticBuffer *DiagClient) {
    272   if (mInitialized)
    273     return;
    274 
    275   mDiagEngine = DiagEngine;
    276   mDiagClient = DiagClient;
    277   mDiag.reset(new clang::Diagnostic(mDiagEngine));
    278   initDiagnostic();
    279   llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
    280 
    281   createTarget(Triple, CPU, Features);
    282   createFileManager();
    283   createSourceManager();
    284 
    285   mInitialized = true;
    286 }
    287 
    288 clang::ModuleLoadResult Slang::loadModule(
    289     clang::SourceLocation ImportLoc,
    290     clang::ModuleIdPath Path,
    291     clang::Module::NameVisibilityKind Visibility,
    292     bool IsInclusionDirective) {
    293   slangAssert(0 && "Not implemented");
    294   return clang::ModuleLoadResult();
    295 }
    296 
    297 bool Slang::setInputSource(llvm::StringRef InputFile,
    298                            const char *Text,
    299                            size_t TextLength) {
    300   mInputFileName = InputFile.str();
    301 
    302   // Reset the ID tables if we are reusing the SourceManager
    303   mSourceMgr->clearIDTables();
    304 
    305   // Load the source
    306   llvm::MemoryBuffer *SB =
    307       llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
    308   mSourceMgr->createMainFileIDForMemBuffer(SB);
    309 
    310   if (mSourceMgr->getMainFileID().isInvalid()) {
    311     mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
    312     return false;
    313   }
    314   return true;
    315 }
    316 
    317 bool Slang::setInputSource(llvm::StringRef InputFile) {
    318   mInputFileName = InputFile.str();
    319 
    320   mSourceMgr->clearIDTables();
    321 
    322   const clang::FileEntry *File = mFileMgr->getFile(InputFile);
    323   if (File)
    324     mSourceMgr->createMainFileID(File);
    325 
    326   if (mSourceMgr->getMainFileID().isInvalid()) {
    327     mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
    328     return false;
    329   }
    330 
    331   return true;
    332 }
    333 
    334 bool Slang::setOutput(const char *OutputFile) {
    335   llvm::sys::Path OutputFilePath(OutputFile);
    336   std::string Error;
    337   llvm::tool_output_file *OS = NULL;
    338 
    339   switch (mOT) {
    340     case OT_Dependency:
    341     case OT_Assembly:
    342     case OT_LLVMAssembly: {
    343       OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine);
    344       break;
    345     }
    346     case OT_Nothing: {
    347       break;
    348     }
    349     case OT_Object:
    350     case OT_Bitcode: {
    351       OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary,
    352                           &Error, mDiagEngine);
    353       break;
    354     }
    355     default: {
    356       llvm_unreachable("Unknown compiler output type");
    357     }
    358   }
    359 
    360   if (!Error.empty())
    361     return false;
    362 
    363   mOS.reset(OS);
    364 
    365   mOutputFileName = OutputFile;
    366 
    367   return true;
    368 }
    369 
    370 bool Slang::setDepOutput(const char *OutputFile) {
    371   llvm::sys::Path OutputFilePath(OutputFile);
    372   std::string Error;
    373 
    374   mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine));
    375   if (!Error.empty() || (mDOS.get() == NULL))
    376     return false;
    377 
    378   mDepOutputFileName = OutputFile;
    379 
    380   return true;
    381 }
    382 
    383 int Slang::generateDepFile() {
    384   if (mDiagEngine->hasErrorOccurred())
    385     return 1;
    386   if (mDOS.get() == NULL)
    387     return 1;
    388 
    389   // Initialize options for generating dependency file
    390   clang::DependencyOutputOptions DepOpts;
    391   DepOpts.IncludeSystemHeaders = 1;
    392   DepOpts.OutputFile = mDepOutputFileName;
    393   DepOpts.Targets = mAdditionalDepTargets;
    394   DepOpts.Targets.push_back(mDepTargetBCFileName);
    395   for (std::vector<std::string>::const_iterator
    396            I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
    397        I != E;
    398        I++) {
    399     DepOpts.Targets.push_back(*I);
    400   }
    401   mGeneratedFileNames.clear();
    402 
    403   // Per-compilation needed initialization
    404   createPreprocessor();
    405   AttachDependencyFileGen(*mPP.get(), DepOpts);
    406 
    407   // Inform the diagnostic client we are processing a source file
    408   mDiagClient->BeginSourceFile(LangOpts, mPP.get());
    409 
    410   // Go through the source file (no operations necessary)
    411   clang::Token Tok;
    412   mPP->EnterMainSourceFile();
    413   do {
    414     mPP->Lex(Tok);
    415   } while (Tok.isNot(clang::tok::eof));
    416 
    417   mPP->EndSourceFile();
    418 
    419   // Declare success if no error
    420   if (!mDiagEngine->hasErrorOccurred())
    421     mDOS->keep();
    422 
    423   // Clean up after compilation
    424   mPP.reset();
    425   mDOS.reset();
    426 
    427   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
    428 }
    429 
    430 int Slang::compile() {
    431   if (mDiagEngine->hasErrorOccurred())
    432     return 1;
    433   if (mOS.get() == NULL)
    434     return 1;
    435 
    436   // Here is per-compilation needed initialization
    437   createPreprocessor();
    438   createASTContext();
    439 
    440   mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT));
    441 
    442   // Inform the diagnostic client we are processing a source file
    443   mDiagClient->BeginSourceFile(LangOpts, mPP.get());
    444 
    445   // The core of the slang compiler
    446   ParseAST(*mPP, mBackend.get(), *mASTContext);
    447 
    448   // Inform the diagnostic client we are done with previous source file
    449   mDiagClient->EndSourceFile();
    450 
    451   // Declare success if no error
    452   if (!mDiagEngine->hasErrorOccurred())
    453     mOS->keep();
    454 
    455   // The compilation ended, clear
    456   mBackend.reset();
    457   mASTContext.reset();
    458   mPP.reset();
    459   mOS.reset();
    460 
    461   return mDiagEngine->hasErrorOccurred() ? 1 : 0;
    462 }
    463 
    464 void Slang::setDebugMetadataEmission(bool EmitDebug) {
    465   if (EmitDebug)
    466     CodeGenOpts.setDebugInfo(clang::CodeGenOptions::FullDebugInfo);
    467   else
    468     CodeGenOpts.setDebugInfo(clang::CodeGenOptions::NoDebugInfo);
    469 }
    470 
    471 void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
    472   CodeGenOpts.OptimizationLevel = OptimizationLevel;
    473 }
    474 
    475 void Slang::reset() {
    476   llvm::errs() << mDiagClient->str();
    477   mDiagEngine->Reset();
    478   mDiagClient->reset();
    479 }
    480 
    481 Slang::~Slang() {
    482   llvm::llvm_shutdown();
    483 }
    484 
    485 }  // namespace slang
    486