| //===- 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 |