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
create(const std::string & Filename,std::string * Err)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
GetBytes(unsigned char * Buf,size_t Len)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
makeStream(const std::string & Filename,std::error_code & EC)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
getReturnValue(ErrorCodes Val)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.
reportFatalErrorThenExitSuccess(void * UserData,const std::string & Reason,bool GenCrashDag)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.
dumpBuildAttributes(Ostream & Str)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
run()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