Home | History | Annotate | Download | only in src
      1 //===- subzero/src/IceCompileServer.cpp - Compile server ------------------===//
      2 //
      3 //                        The Subzero Code Generator
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 ///
     10 /// \file
     11 /// \brief Defines the basic commandline-based compile server.
     12 ///
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "IceCompileServer.h"
     16 
     17 #include "IceASanInstrumentation.h"
     18 #include "IceClFlags.h"
     19 #include "IceELFStreamer.h"
     20 #include "IceGlobalContext.h"
     21 #include "IceRevision.h"
     22 #include "LinuxMallocProfiling.h"
     23 
     24 #ifdef __clang__
     25 #pragma clang diagnostic push
     26 #pragma clang diagnostic ignored "-Wunused-parameter"
     27 #endif // __clang__
     28 
     29 #ifdef PNACL_LLVM
     30 #include "llvm/Bitcode/NaCl/NaClBitcodeMungeUtils.h"
     31 #endif // PNACL_LLVM
     32 #include "llvm/Support/FileSystem.h"
     33 #include "llvm/Support/raw_os_ostream.h"
     34 #include "llvm/Support/Signals.h"
     35 #include "llvm/Support/SourceMgr.h"
     36 #include "llvm/Support/StreamingMemoryObject.h"
     37 
     38 #ifdef __clang__
     39 #pragma clang diagnostic pop
     40 #endif // __clang__
     41 
     42 #include <cstdio>
     43 #include <fstream>
     44 #include <iostream>
     45 #include <thread>
     46 
     47 namespace Ice {
     48 
     49 namespace {
     50 
     51 // Define a SmallVector backed buffer as a data stream, so that it can hold the
     52 // generated binary version of the textual bitcode in the input file.
     53 class TextDataStreamer : public llvm::DataStreamer {
     54 public:
     55   TextDataStreamer() = default;
     56   ~TextDataStreamer() final = default;
     57 #ifdef PNACL_LLVM
     58   using CreateType = TextDataStreamer *;
     59 #else  // !PNACL_LLVM
     60   using CreateType = std::unique_ptr<TextDataStreamer>;
     61 #endif // !PNACL_LLVM
     62   static CreateType create(const std::string &Filename, std::string *Err);
     63   size_t GetBytes(unsigned char *Buf, size_t Len) final;
     64 
     65 private:
     66   llvm::SmallVector<char, 1024> BitcodeBuffer;
     67   size_t Cursor = 0;
     68 };
     69 
     70 TextDataStreamer::CreateType
     71 TextDataStreamer::create(const std::string &Filename, std::string *Err) {
     72 #ifdef PNACL_LLVM
     73   TextDataStreamer *Streamer = new TextDataStreamer();
     74   llvm::raw_string_ostream ErrStrm(*Err);
     75   if (std::error_code EC = llvm::readNaClRecordTextAndBuildBitcode(
     76           Filename, Streamer->BitcodeBuffer, &ErrStrm)) {
     77     ErrStrm << EC.message();
     78     ErrStrm.flush();
     79     delete Streamer;
     80     return nullptr;
     81   }
     82   ErrStrm.flush();
     83   return Streamer;
     84 #else  // !PNACL_LLVM
     85   return CreateType();
     86 #endif // !PNACL_LLVM
     87 }
     88 
     89 size_t TextDataStreamer::GetBytes(unsigned char *Buf, size_t Len) {
     90   if (Cursor >= BitcodeBuffer.size())
     91     return 0;
     92   size_t Remaining = BitcodeBuffer.size();
     93   Len = std::min(Len, Remaining);
     94   for (size_t i = 0; i < Len; ++i)
     95     Buf[i] = BitcodeBuffer[Cursor + i];
     96   Cursor += Len;
     97   return Len;
     98 }
     99 
    100 std::unique_ptr<Ostream> makeStream(const std::string &Filename,
    101                                     std::error_code &EC) {
    102   if (Filename == "-") {
    103     return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout));
    104   } else if (Filename == "/dev/stderr") {
    105     return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cerr));
    106   } else {
    107     return std::unique_ptr<Ostream>(
    108         new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None));
    109   }
    110 }
    111 
    112 ErrorCodes getReturnValue(ErrorCodes Val) {
    113   if (getFlags().getAlwaysExitSuccess())
    114     return EC_None;
    115   return Val;
    116 }
    117 
    118 // Reports fatal error message, and then exits with success status 0.
    119 void reportFatalErrorThenExitSuccess(void *UserData, const std::string &Reason,
    120                                      bool GenCrashDag) {
    121   (void)UserData;
    122   (void)GenCrashDag;
    123 
    124   // Note: This code is (mostly) copied from llvm/lib/Support/ErrorHandling.cpp
    125 
    126   // Blast the result out to stderr.  We don't try hard to make sure this
    127   // succeeds (e.g. handling EINTR) and we can't use errs() here because
    128   // raw ostreams can call report_fatal_error.
    129   llvm::SmallVector<char, 64> Buffer;
    130   llvm::raw_svector_ostream OS(Buffer);
    131   OS << "LLVM ERROR: " << Reason << "\n";
    132   llvm::StringRef MessageStr = OS.str();
    133   ssize_t Written =
    134       std::fwrite(MessageStr.data(), sizeof(char), MessageStr.size(), stderr);
    135   (void)Written; // If something went wrong, we deliberately just give up.
    136 
    137   // If we reached here, we are failing ungracefully. Run the interrupt handlers
    138   // to make sure any special cleanups get done, in particular that we remove
    139   // files registered with RemoveFileOnSignal.
    140   llvm::sys::RunInterruptHandlers();
    141 
    142   exit(0);
    143 }
    144 
    145 struct {
    146   const char *FlagName;
    147   bool FlagValue;
    148 } ConditionalBuildAttributes[] = {
    149     {"dump", BuildDefs::dump()},
    150     {"llvm_cl", BuildDefs::llvmCl()},
    151     {"llvm_ir", BuildDefs::llvmIr()},
    152     {"llvm_ir_as_input", BuildDefs::llvmIrAsInput()},
    153     {"minimal_build", BuildDefs::minimal()},
    154     {"browser_mode", BuildDefs::browser()}};
    155 
    156 /// Dumps values of build attributes to Stream if Stream is non-null.
    157 void dumpBuildAttributes(Ostream &Str) {
    158 // List the supported targets.
    159 #define SUBZERO_TARGET(TARGET) Str << "target_" XSTRINGIFY(TARGET) "\n";
    160 #include "SZTargets.def"
    161   const char *Prefix[2] = {"no", "allow"};
    162   for (size_t i = 0; i < llvm::array_lengthof(ConditionalBuildAttributes);
    163        ++i) {
    164     const auto &A = ConditionalBuildAttributes[i];
    165     Str << Prefix[A.FlagValue] << "_" << A.FlagName << "\n";
    166   }
    167   Str << "revision_" << getSubzeroRevision() << "\n";
    168 }
    169 
    170 } // end of anonymous namespace
    171 
    172 void CLCompileServer::run() {
    173   if (BuildDefs::dump()) {
    174 #ifdef PNACL_LLVM
    175     llvm::sys::PrintStackTraceOnErrorSignal();
    176 #else  // !PNACL_LLVM
    177     llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
    178 #endif // !PNACL_LLVM
    179   }
    180   ClFlags::parseFlags(argc, argv);
    181   ClFlags &Flags = ClFlags::Flags;
    182   ClFlags::getParsedClFlags(Flags);
    183 
    184   // Override report_fatal_error if we want to exit with 0 status.
    185   if (Flags.getAlwaysExitSuccess())
    186     llvm::install_fatal_error_handler(reportFatalErrorThenExitSuccess, this);
    187 
    188   std::error_code EC;
    189   std::unique_ptr<Ostream> Ls = makeStream(Flags.getLogFilename(), EC);
    190   if (EC) {
    191     llvm::report_fatal_error("Unable to open log file");
    192   }
    193   Ls->SetUnbuffered();
    194   Ice::LinuxMallocProfiling _(Flags.getNumTranslationThreads(), Ls.get());
    195 
    196   std::unique_ptr<Ostream> Os;
    197   std::unique_ptr<ELFStreamer> ELFStr;
    198   switch (Flags.getOutFileType()) {
    199   case FT_Elf: {
    200     if (Flags.getOutputFilename() == "-" && !Flags.getGenerateBuildAtts()) {
    201       *Ls << "Error: writing binary ELF to stdout is unsupported\n";
    202       return transferErrorCode(getReturnValue(Ice::EC_Args));
    203     }
    204     std::unique_ptr<llvm::raw_fd_ostream> FdOs(new llvm::raw_fd_ostream(
    205         Flags.getOutputFilename(), EC, llvm::sys::fs::F_None));
    206     if (EC) {
    207       *Ls << "Failed to open output file: " << Flags.getOutputFilename()
    208           << ":\n" << EC.message() << "\n";
    209       return transferErrorCode(getReturnValue(Ice::EC_Args));
    210     }
    211     ELFStr.reset(new ELFFileStreamer(*FdOs.get()));
    212     Os.reset(FdOs.release());
    213     // NaCl sets st_blksize to 0, and LLVM uses that to pick the default
    214     // preferred buffer size. Set to something non-zero.
    215     Os->SetBufferSize(1 << 14);
    216   } break;
    217   case FT_Asm:
    218   case FT_Iasm: {
    219     Os = makeStream(Flags.getOutputFilename(), EC);
    220     if (EC) {
    221       *Ls << "Failed to open output file: " << Flags.getOutputFilename()
    222           << ":\n" << EC.message() << "\n";
    223       return transferErrorCode(getReturnValue(Ice::EC_Args));
    224     }
    225     Os->SetUnbuffered();
    226   } break;
    227   }
    228 
    229   if (BuildDefs::minimal() && Flags.getBitcodeAsText())
    230     llvm::report_fatal_error("Can't specify 'bitcode-as-text' flag in "
    231                              "minimal build");
    232 
    233   std::string StrError;
    234   std::unique_ptr<llvm::DataStreamer> InputStream(
    235       (!BuildDefs::minimal() && Flags.getBitcodeAsText())
    236           ? TextDataStreamer::create(Flags.getIRFilename(), &StrError)
    237           : llvm::getDataFileStreamer(Flags.getIRFilename(), &StrError));
    238   if (!StrError.empty() || !InputStream) {
    239     llvm::SMDiagnostic Err(Flags.getIRFilename(), llvm::SourceMgr::DK_Error,
    240                            StrError);
    241     Err.print(Flags.getAppName().c_str(), *Ls);
    242     return transferErrorCode(getReturnValue(Ice::EC_Bitcode));
    243   }
    244 
    245   if (Flags.getGenerateBuildAtts()) {
    246     dumpBuildAttributes(*Os.get());
    247     return transferErrorCode(getReturnValue(Ice::EC_None));
    248   }
    249 
    250   Ctx.reset(new GlobalContext(Ls.get(), Os.get(), Ls.get(), ELFStr.get()));
    251 
    252   if (!BuildDefs::minimal() && getFlags().getSanitizeAddresses()) {
    253     std::unique_ptr<Instrumentation> Instr(new ASanInstrumentation(Ctx.get()));
    254     Ctx->setInstrumentation(std::move(Instr));
    255   }
    256 
    257   if (getFlags().getNumTranslationThreads() != 0) {
    258     std::thread CompileThread([this, &Flags, &InputStream]() {
    259       Ctx->initParserThread();
    260       getCompiler().run(Flags, *Ctx.get(), std::move(InputStream));
    261     });
    262     CompileThread.join();
    263   } else {
    264     getCompiler().run(Flags, *Ctx.get(), std::move(InputStream));
    265   }
    266   transferErrorCode(
    267       getReturnValue(static_cast<ErrorCodes>(Ctx->getErrorStatus()->value())));
    268   Ctx->dumpConstantLookupCounts();
    269   Ctx->dumpStrings();
    270 }
    271 
    272 } // end of namespace Ice
    273