//===- subzero/src/IceCompileServer.cpp - Compile server ------------------===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Defines the basic commandline-based compile server. /// //===----------------------------------------------------------------------===// #include "IceCompileServer.h" #include "IceASanInstrumentation.h" #include "IceClFlags.h" #include "IceELFStreamer.h" #include "IceGlobalContext.h" #include "IceRevision.h" #include "LinuxMallocProfiling.h" #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #endif // __clang__ #ifdef PNACL_LLVM #include "llvm/Bitcode/NaCl/NaClBitcodeMungeUtils.h" #endif // PNACL_LLVM #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/StreamingMemoryObject.h" #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ #include <cstdio> #include <fstream> #include <iostream> #include <thread> namespace Ice { namespace { // Define a SmallVector backed buffer as a data stream, so that it can hold the // generated binary version of the textual bitcode in the input file. class TextDataStreamer : public llvm::DataStreamer { public: TextDataStreamer() = default; ~TextDataStreamer() final = default; #ifdef PNACL_LLVM using CreateType = TextDataStreamer *; #else // !PNACL_LLVM using CreateType = std::unique_ptr<TextDataStreamer>; #endif // !PNACL_LLVM static CreateType create(const std::string &Filename, std::string *Err); size_t GetBytes(unsigned char *Buf, size_t Len) final; private: llvm::SmallVector<char, 1024> BitcodeBuffer; size_t Cursor = 0; }; TextDataStreamer::CreateType TextDataStreamer::create(const std::string &Filename, std::string *Err) { #ifdef PNACL_LLVM TextDataStreamer *Streamer = new TextDataStreamer(); llvm::raw_string_ostream ErrStrm(*Err); if (std::error_code EC = llvm::readNaClRecordTextAndBuildBitcode( Filename, Streamer->BitcodeBuffer, &ErrStrm)) { ErrStrm << EC.message(); ErrStrm.flush(); delete Streamer; return nullptr; } ErrStrm.flush(); return Streamer; #else // !PNACL_LLVM return CreateType(); #endif // !PNACL_LLVM } size_t TextDataStreamer::GetBytes(unsigned char *Buf, size_t Len) { if (Cursor >= BitcodeBuffer.size()) return 0; size_t Remaining = BitcodeBuffer.size(); Len = std::min(Len, Remaining); for (size_t i = 0; i < Len; ++i) Buf[i] = BitcodeBuffer[Cursor + i]; Cursor += Len; return Len; } std::unique_ptr<Ostream> makeStream(const std::string &Filename, std::error_code &EC) { if (Filename == "-") { return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout)); } else if (Filename == "/dev/stderr") { return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cerr)); } else { return std::unique_ptr<Ostream>( new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None)); } } ErrorCodes getReturnValue(ErrorCodes Val) { if (getFlags().getAlwaysExitSuccess()) return EC_None; return Val; } // Reports fatal error message, and then exits with success status 0. void reportFatalErrorThenExitSuccess(void *UserData, const std::string &Reason, bool GenCrashDag) { (void)UserData; (void)GenCrashDag; // Note: This code is (mostly) copied from llvm/lib/Support/ErrorHandling.cpp // Blast the result out to stderr. We don't try hard to make sure this // succeeds (e.g. handling EINTR) and we can't use errs() here because // raw ostreams can call report_fatal_error. llvm::SmallVector<char, 64> Buffer; llvm::raw_svector_ostream OS(Buffer); OS << "LLVM ERROR: " << Reason << "\n"; llvm::StringRef MessageStr = OS.str(); ssize_t Written = std::fwrite(MessageStr.data(), sizeof(char), MessageStr.size(), stderr); (void)Written; // If something went wrong, we deliberately just give up. // If we reached here, we are failing ungracefully. Run the interrupt handlers // to make sure any special cleanups get done, in particular that we remove // files registered with RemoveFileOnSignal. llvm::sys::RunInterruptHandlers(); exit(0); } struct { const char *FlagName; bool FlagValue; } ConditionalBuildAttributes[] = { {"dump", BuildDefs::dump()}, {"llvm_cl", BuildDefs::llvmCl()}, {"llvm_ir", BuildDefs::llvmIr()}, {"llvm_ir_as_input", BuildDefs::llvmIrAsInput()}, {"minimal_build", BuildDefs::minimal()}, {"browser_mode", BuildDefs::browser()}}; /// Dumps values of build attributes to Stream if Stream is non-null. void dumpBuildAttributes(Ostream &Str) { // List the supported targets. #define SUBZERO_TARGET(TARGET) Str << "target_" XSTRINGIFY(TARGET) "\n"; #include "SZTargets.def" const char *Prefix[2] = {"no", "allow"}; for (size_t i = 0; i < llvm::array_lengthof(ConditionalBuildAttributes); ++i) { const auto &A = ConditionalBuildAttributes[i]; Str << Prefix[A.FlagValue] << "_" << A.FlagName << "\n"; } Str << "revision_" << getSubzeroRevision() << "\n"; } } // end of anonymous namespace void CLCompileServer::run() { if (BuildDefs::dump()) { #ifdef PNACL_LLVM llvm::sys::PrintStackTraceOnErrorSignal(); #else // !PNACL_LLVM llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); #endif // !PNACL_LLVM } ClFlags::parseFlags(argc, argv); ClFlags &Flags = ClFlags::Flags; ClFlags::getParsedClFlags(Flags); // Override report_fatal_error if we want to exit with 0 status. if (Flags.getAlwaysExitSuccess()) llvm::install_fatal_error_handler(reportFatalErrorThenExitSuccess, this); std::error_code EC; std::unique_ptr<Ostream> Ls = makeStream(Flags.getLogFilename(), EC); if (EC) { llvm::report_fatal_error("Unable to open log file"); } Ls->SetUnbuffered(); Ice::LinuxMallocProfiling _(Flags.getNumTranslationThreads(), Ls.get()); std::unique_ptr<Ostream> Os; std::unique_ptr<ELFStreamer> ELFStr; switch (Flags.getOutFileType()) { case FT_Elf: { if (Flags.getOutputFilename() == "-" && !Flags.getGenerateBuildAtts()) { *Ls << "Error: writing binary ELF to stdout is unsupported\n"; return transferErrorCode(getReturnValue(Ice::EC_Args)); } std::unique_ptr<llvm::raw_fd_ostream> FdOs(new llvm::raw_fd_ostream( Flags.getOutputFilename(), EC, llvm::sys::fs::F_None)); if (EC) { *Ls << "Failed to open output file: " << Flags.getOutputFilename() << ":\n" << EC.message() << "\n"; return transferErrorCode(getReturnValue(Ice::EC_Args)); } ELFStr.reset(new ELFFileStreamer(*FdOs.get())); Os.reset(FdOs.release()); // NaCl sets st_blksize to 0, and LLVM uses that to pick the default // preferred buffer size. Set to something non-zero. Os->SetBufferSize(1 << 14); } break; case FT_Asm: case FT_Iasm: { Os = makeStream(Flags.getOutputFilename(), EC); if (EC) { *Ls << "Failed to open output file: " << Flags.getOutputFilename() << ":\n" << EC.message() << "\n"; return transferErrorCode(getReturnValue(Ice::EC_Args)); } Os->SetUnbuffered(); } break; } if (BuildDefs::minimal() && Flags.getBitcodeAsText()) llvm::report_fatal_error("Can't specify 'bitcode-as-text' flag in " "minimal build"); std::string StrError; std::unique_ptr<llvm::DataStreamer> InputStream( (!BuildDefs::minimal() && Flags.getBitcodeAsText()) ? TextDataStreamer::create(Flags.getIRFilename(), &StrError) : llvm::getDataFileStreamer(Flags.getIRFilename(), &StrError)); if (!StrError.empty() || !InputStream) { llvm::SMDiagnostic Err(Flags.getIRFilename(), llvm::SourceMgr::DK_Error, StrError); Err.print(Flags.getAppName().c_str(), *Ls); return transferErrorCode(getReturnValue(Ice::EC_Bitcode)); } if (Flags.getGenerateBuildAtts()) { dumpBuildAttributes(*Os.get()); return transferErrorCode(getReturnValue(Ice::EC_None)); } Ctx.reset(new GlobalContext(Ls.get(), Os.get(), Ls.get(), ELFStr.get())); if (!BuildDefs::minimal() && getFlags().getSanitizeAddresses()) { std::unique_ptr<Instrumentation> Instr(new ASanInstrumentation(Ctx.get())); Ctx->setInstrumentation(std::move(Instr)); } if (getFlags().getNumTranslationThreads() != 0) { std::thread CompileThread([this, &Flags, &InputStream]() { Ctx->initParserThread(); getCompiler().run(Flags, *Ctx.get(), std::move(InputStream)); }); CompileThread.join(); } else { getCompiler().run(Flags, *Ctx.get(), std::move(InputStream)); } transferErrorCode( getReturnValue(static_cast<ErrorCodes>(Ctx->getErrorStatus()->value()))); Ctx->dumpConstantLookupCounts(); Ctx->dumpStrings(); } } // end of namespace Ice