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
47namespace Ice {
48
49namespace {
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.
53class TextDataStreamer : public llvm::DataStreamer {
54public:
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
65private:
66  llvm::SmallVector<char, 1024> BitcodeBuffer;
67  size_t Cursor = 0;
68};
69
70TextDataStreamer::CreateType
71TextDataStreamer::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
89size_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
100std::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
112ErrorCodes 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.
119void 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
145struct {
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.
157void 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
172void 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