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