| OLD | NEW |
| (Empty) |
| 1 //===- subzero/src/llvm2ice.cpp - Driver for testing ----------------------===// | |
| 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 // This file defines a driver that uses LLVM capabilities to parse a | |
| 11 // bitcode file and build the LLVM IR, and then convert the LLVM basic | |
| 12 // blocks, instructions, and operands into their Subzero equivalents. | |
| 13 // | |
| 14 //===----------------------------------------------------------------------===// | |
| 15 | |
| 16 #include <fstream> | |
| 17 #include <iostream> | |
| 18 | |
| 19 #include "llvm/ADT/STLExtras.h" | |
| 20 #include "llvm/IR/LLVMContext.h" | |
| 21 #include "llvm/IRReader/IRReader.h" | |
| 22 #include "llvm/Support/CommandLine.h" | |
| 23 #include "llvm/Support/FileSystem.h" | |
| 24 #include "llvm/Support/raw_os_ostream.h" | |
| 25 #include "llvm/Support/SourceMgr.h" | |
| 26 | |
| 27 #include "IceCfg.h" | |
| 28 #include "IceClFlags.h" | |
| 29 #include "IceConverter.h" | |
| 30 #include "IceELFObjectWriter.h" | |
| 31 #include "IceELFStreamer.h" | |
| 32 #include "PNaClTranslator.h" | |
| 33 | |
| 34 using namespace llvm; | |
| 35 | |
| 36 static cl::list<Ice::VerboseItem> VerboseList( | |
| 37 "verbose", cl::CommaSeparated, | |
| 38 cl::desc("Verbose options (can be comma-separated):"), | |
| 39 cl::values( | |
| 40 clEnumValN(Ice::IceV_Instructions, "inst", "Print basic instructions"), | |
| 41 clEnumValN(Ice::IceV_Deleted, "del", "Include deleted instructions"), | |
| 42 clEnumValN(Ice::IceV_InstNumbers, "instnum", | |
| 43 "Print instruction numbers"), | |
| 44 clEnumValN(Ice::IceV_Preds, "pred", "Show predecessors"), | |
| 45 clEnumValN(Ice::IceV_Succs, "succ", "Show successors"), | |
| 46 clEnumValN(Ice::IceV_Liveness, "live", "Liveness information"), | |
| 47 clEnumValN(Ice::IceV_RegOrigins, "orig", "Physical register origins"), | |
| 48 clEnumValN(Ice::IceV_LinearScan, "regalloc", "Linear scan details"), | |
| 49 clEnumValN(Ice::IceV_Frame, "frame", "Stack frame layout details"), | |
| 50 clEnumValN(Ice::IceV_AddrOpt, "addropt", "Address mode optimization"), | |
| 51 clEnumValN(Ice::IceV_Random, "random", "Randomization details"), | |
| 52 clEnumValN(Ice::IceV_All, "all", "Use all verbose options"), | |
| 53 clEnumValN(Ice::IceV_Most, "most", | |
| 54 "Use all verbose options except 'regalloc' and 'time'"), | |
| 55 clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd)); | |
| 56 static cl::opt<Ice::TargetArch> TargetArch( | |
| 57 "target", cl::desc("Target architecture:"), cl::init(Ice::Target_X8632), | |
| 58 cl::values( | |
| 59 clEnumValN(Ice::Target_X8632, "x8632", "x86-32"), | |
| 60 clEnumValN(Ice::Target_X8632, "x86-32", "x86-32 (same as x8632)"), | |
| 61 clEnumValN(Ice::Target_X8632, "x86_32", "x86-32 (same as x8632)"), | |
| 62 clEnumValN(Ice::Target_X8664, "x8664", "x86-64"), | |
| 63 clEnumValN(Ice::Target_X8664, "x86-64", "x86-64 (same as x8664)"), | |
| 64 clEnumValN(Ice::Target_X8664, "x86_64", "x86-64 (same as x8664)"), | |
| 65 clEnumValN(Ice::Target_ARM32, "arm", "arm32"), | |
| 66 clEnumValN(Ice::Target_ARM32, "arm32", "arm32 (same as arm)"), | |
| 67 clEnumValN(Ice::Target_ARM64, "arm64", "arm64"), clEnumValEnd)); | |
| 68 static cl::opt<bool> UseSandboxing("sandbox", cl::desc("Use sandboxing")); | |
| 69 static cl::opt<bool> | |
| 70 FunctionSections("ffunction-sections", | |
| 71 cl::desc("Emit functions into separate sections")); | |
| 72 static cl::opt<bool> | |
| 73 DataSections("fdata-sections", | |
| 74 cl::desc("Emit (global) data into separate sections")); | |
| 75 static cl::opt<Ice::OptLevel> | |
| 76 OptLevel(cl::desc("Optimization level"), cl::init(Ice::Opt_m1), | |
| 77 cl::value_desc("level"), | |
| 78 cl::values(clEnumValN(Ice::Opt_m1, "Om1", "-1"), | |
| 79 clEnumValN(Ice::Opt_m1, "O-1", "-1"), | |
| 80 clEnumValN(Ice::Opt_0, "O0", "0"), | |
| 81 clEnumValN(Ice::Opt_1, "O1", "1"), | |
| 82 clEnumValN(Ice::Opt_2, "O2", "2"), clEnumValEnd)); | |
| 83 static cl::opt<std::string> IRFilename(cl::Positional, cl::desc("<IR file>"), | |
| 84 cl::init("-")); | |
| 85 static cl::opt<std::string> OutputFilename("o", | |
| 86 cl::desc("Override output filename"), | |
| 87 cl::init("-"), | |
| 88 cl::value_desc("filename")); | |
| 89 static cl::opt<std::string> LogFilename("log", cl::desc("Set log filename"), | |
| 90 cl::init("-"), | |
| 91 cl::value_desc("filename")); | |
| 92 static cl::opt<std::string> | |
| 93 TestPrefix("prefix", | |
| 94 cl::desc("Prepend a prefix to symbol names for testing"), | |
| 95 cl::init(""), cl::value_desc("prefix")); | |
| 96 static cl::opt<bool> DisableInternal("externalize", | |
| 97 cl::desc("Externalize all symbols")); | |
| 98 static cl::opt<bool> | |
| 99 DisableTranslation("notranslate", cl::desc("Disable Subzero translation")); | |
| 100 // Note: Modifiable only if ALLOW_DISABLE_IR_GEN. | |
| 101 static cl::opt<bool> | |
| 102 DisableIRGeneration("no-ir-gen", | |
| 103 cl::desc("Disable generating Subzero IR.")); | |
| 104 static cl::opt<std::string> | |
| 105 TranslateOnly("translate-only", | |
| 106 cl::desc("Translate only the given function"), cl::init("")); | |
| 107 | |
| 108 static cl::opt<bool> SubzeroTimingEnabled( | |
| 109 "timing", cl::desc("Enable breakdown timing of Subzero translation")); | |
| 110 | |
| 111 static cl::opt<bool> TimeEachFunction( | |
| 112 "timing-funcs", cl::desc("Print total translation time for each function")); | |
| 113 | |
| 114 static cl::opt<std::string> TimingFocusOn( | |
| 115 "timing-focus", | |
| 116 cl::desc("Break down timing for a specific function (use '*' for all)"), | |
| 117 cl::init("")); | |
| 118 | |
| 119 static cl::opt<std::string> VerboseFocusOn( | |
| 120 "verbose-focus", | |
| 121 cl::desc("Temporarily enable full verbosity for a specific function"), | |
| 122 cl::init("")); | |
| 123 | |
| 124 static cl::opt<bool> | |
| 125 EnablePhiEdgeSplit("phi-edge-split", | |
| 126 cl::desc("Enable edge splitting for Phi lowering"), | |
| 127 cl::init(true)); | |
| 128 | |
| 129 static cl::opt<bool> DecorateAsm( | |
| 130 "asm-verbose", | |
| 131 cl::desc("Decorate textual asm output with register liveness info")); | |
| 132 | |
| 133 static cl::opt<bool> | |
| 134 DumpStats("szstats", | |
| 135 cl::desc("Print statistics after translating each function")); | |
| 136 | |
| 137 // This is currently needed by crosstest.py. | |
| 138 static cl::opt<bool> AllowUninitializedGlobals( | |
| 139 "allow-uninitialized-globals", | |
| 140 cl::desc("Allow global variables to be uninitialized")); | |
| 141 | |
| 142 static cl::opt<NaClFileFormat> InputFileFormat( | |
| 143 "bitcode-format", cl::desc("Define format of input file:"), | |
| 144 cl::values(clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"), | |
| 145 clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"), | |
| 146 clEnumValEnd), | |
| 147 cl::init(LLVMFormat)); | |
| 148 | |
| 149 static cl::opt<std::string> | |
| 150 DefaultGlobalPrefix("default-global-prefix", | |
| 151 cl::desc("Define default global prefix for naming " | |
| 152 "unnamed globals"), | |
| 153 cl::init("Global")); | |
| 154 | |
| 155 static cl::opt<std::string> | |
| 156 DefaultFunctionPrefix("default-function-prefix", | |
| 157 cl::desc("Define default function prefix for naming " | |
| 158 "unnamed functions"), | |
| 159 cl::init("Function")); | |
| 160 | |
| 161 // Note: While this flag isn't used in the minimal build, we keep this | |
| 162 // flag so that tests can set this command-line flag without concern | |
| 163 // to the type of build. We double check that this flag at runtime | |
| 164 // to make sure the consistency is maintained. | |
| 165 static cl::opt<bool> | |
| 166 BuildOnRead("build-on-read", | |
| 167 cl::desc("Build ICE instructions when reading bitcode"), | |
| 168 cl::init(true)); | |
| 169 | |
| 170 static cl::opt<bool> AllowErrorRecovery( | |
| 171 "allow-pnacl-reader-error-recovery", | |
| 172 cl::desc("Allow error recovery when reading PNaCl bitcode."), | |
| 173 cl::init(false)); | |
| 174 | |
| 175 // TODO(kschimpf) Remove once the emitter handles these cases. | |
| 176 static cl::opt<bool> | |
| 177 StubConstantCalls("stub-const-calls", | |
| 178 cl::desc("Stub indirect calls to constants."), | |
| 179 cl::init(false)); | |
| 180 | |
| 181 static cl::opt<bool> LLVMVerboseErrors( | |
| 182 "verbose-llvm-parse-errors", | |
| 183 cl::desc("Print out more descriptive PNaCl bitcode parse errors when " | |
| 184 "building LLVM IR first"), | |
| 185 cl::init(false)); | |
| 186 | |
| 187 static cl::opt<Ice::FileType> OutFileType( | |
| 188 "filetype", cl::desc("Output file type"), cl::init(Ice::FT_Iasm), | |
| 189 cl::values(clEnumValN(Ice::FT_Elf, "obj", "Native ELF object ('.o') file"), | |
| 190 clEnumValN(Ice::FT_Asm, "asm", "Assembly ('.s') file"), | |
| 191 clEnumValN(Ice::FT_Iasm, "iasm", | |
| 192 "Low-level integrated assembly ('.s') file"), | |
| 193 clEnumValEnd)); | |
| 194 | |
| 195 static cl::opt<bool> AlwaysExitSuccess( | |
| 196 "exit-success", cl::desc("Exit with success status, even if errors found"), | |
| 197 cl::init(false)); | |
| 198 | |
| 199 static cl::opt<bool> GenerateBuildAtts( | |
| 200 "build-atts", cl::desc("Generate list of build attributes associated with " | |
| 201 "this executable."), | |
| 202 cl::init(false)); | |
| 203 | |
| 204 // Number of translation threads (in addition to the parser thread and | |
| 205 // the emitter thread). The special case of 0 means purely | |
| 206 // sequential, i.e. parser, translator, and emitter all within the | |
| 207 // same single thread. (This may need a slight rework if we expand to | |
| 208 // multiple parser or emitter threads.) | |
| 209 static cl::opt<uint32_t> NumThreads( | |
| 210 "threads", | |
| 211 cl::desc("Number of translation threads (0 for purely sequential)"), | |
| 212 // TODO(stichnot): Settle on a good default. Consider | |
| 213 // something related to std::thread::hardware_concurrency(). | |
| 214 cl::init(2)); | |
| 215 | |
| 216 static int GetReturnValue(int Val) { | |
| 217 if (AlwaysExitSuccess) | |
| 218 return 0; | |
| 219 return Val; | |
| 220 } | |
| 221 | |
| 222 static struct { | |
| 223 const char *FlagName; | |
| 224 int FlagValue; | |
| 225 } ConditionalBuildAttributes[] = {{"dump", ALLOW_DUMP}, | |
| 226 {"disable_ir_gen", ALLOW_DISABLE_IR_GEN}, | |
| 227 {"llvm_cl", ALLOW_LLVM_CL}, | |
| 228 {"llvm_ir", ALLOW_LLVM_IR}, | |
| 229 {"llvm_ir_as_input", ALLOW_LLVM_IR_AS_INPUT}, | |
| 230 {"minimal_build", ALLOW_MINIMAL_BUILD}}; | |
| 231 | |
| 232 // Validates values of build attributes. Prints them to Stream if | |
| 233 // Stream is non-null. | |
| 234 static void ValidateAndGenerateBuildAttributes(Ice::Ostream *Stream) { | |
| 235 | |
| 236 if (Stream) | |
| 237 *Stream << TargetArch << "\n"; | |
| 238 | |
| 239 for (size_t i = 0; i < array_lengthof(ConditionalBuildAttributes); ++i) { | |
| 240 switch (ConditionalBuildAttributes[i].FlagValue) { | |
| 241 case 0: | |
| 242 if (Stream) | |
| 243 *Stream << "no_" << ConditionalBuildAttributes[i].FlagName << "\n"; | |
| 244 break; | |
| 245 case 1: | |
| 246 if (Stream) | |
| 247 *Stream << "allow_" << ConditionalBuildAttributes[i].FlagName << "\n"; | |
| 248 break; | |
| 249 default: { | |
| 250 std::string Buffer; | |
| 251 raw_string_ostream StrBuf(Buffer); | |
| 252 StrBuf << "Flag " << ConditionalBuildAttributes[i].FlagName | |
| 253 << " must be defined as 0/1. Found: " | |
| 254 << ConditionalBuildAttributes[i].FlagValue; | |
| 255 report_fatal_error(StrBuf.str()); | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 int main(int argc, char **argv) { | |
| 262 | |
| 263 cl::ParseCommandLineOptions(argc, argv); | |
| 264 | |
| 265 if (DisableIRGeneration) | |
| 266 DisableTranslation = true; | |
| 267 | |
| 268 Ice::VerboseMask VMask = Ice::IceV_None; | |
| 269 // Don't generate verbose messages if routines | |
| 270 // to dump messages are not available. | |
| 271 if (ALLOW_DUMP) { | |
| 272 for (unsigned i = 0; i != VerboseList.size(); ++i) | |
| 273 VMask |= VerboseList[i]; | |
| 274 } | |
| 275 | |
| 276 std::ofstream Lfs; | |
| 277 std::unique_ptr<Ice::Ostream> Ls; | |
| 278 if (LogFilename != "-") { | |
| 279 Lfs.open(LogFilename.c_str(), std::ofstream::out); | |
| 280 Ls.reset(new raw_os_ostream(Lfs)); | |
| 281 } else { | |
| 282 Ls.reset(new raw_os_ostream(std::cout)); | |
| 283 } | |
| 284 Ls->SetUnbuffered(); | |
| 285 | |
| 286 ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr); | |
| 287 if (GenerateBuildAtts) | |
| 288 return GetReturnValue(Ice::EC_None); | |
| 289 | |
| 290 if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) { | |
| 291 *Ls << "Error: Build doesn't allow --no-ir-gen when not " | |
| 292 << "ALLOW_DISABLE_IR_GEN!\n"; | |
| 293 return GetReturnValue(Ice::EC_Args); | |
| 294 } | |
| 295 | |
| 296 Ice::ClFlags Flags; | |
| 297 Flags.setAllowErrorRecovery(AllowErrorRecovery); | |
| 298 Flags.setAllowUninitializedGlobals(AllowUninitializedGlobals); | |
| 299 Flags.setDataSections(DataSections); | |
| 300 Flags.setDecorateAsm(DecorateAsm); | |
| 301 Flags.setDefaultFunctionPrefix(DefaultFunctionPrefix); | |
| 302 Flags.setDefaultGlobalPrefix(DefaultGlobalPrefix); | |
| 303 Flags.setDisableInternal(DisableInternal); | |
| 304 Flags.setDisableIRGeneration(DisableIRGeneration); | |
| 305 Flags.setDisableTranslation(DisableTranslation); | |
| 306 Flags.setDumpStats(DumpStats); | |
| 307 Flags.setFunctionSections(FunctionSections); | |
| 308 Flags.setNumTranslationThreads(NumThreads); | |
| 309 Flags.setPhiEdgeSplit(EnablePhiEdgeSplit); | |
| 310 Flags.setStubConstantCalls(StubConstantCalls); | |
| 311 Flags.setSubzeroTimingEnabled(SubzeroTimingEnabled); | |
| 312 Flags.setTimeEachFunction(TimeEachFunction); | |
| 313 Flags.setTimingFocusOn(TimingFocusOn); | |
| 314 Flags.setTranslateOnly(TranslateOnly); | |
| 315 Flags.setUseSandboxing(UseSandboxing); | |
| 316 Flags.setVerboseFocusOn(VerboseFocusOn); | |
| 317 Flags.setOutFileType(OutFileType); | |
| 318 | |
| 319 // Force -build-on-read=0 for .ll files. | |
| 320 const std::string LLSuffix = ".ll"; | |
| 321 if (IRFilename.length() >= LLSuffix.length() && | |
| 322 IRFilename.compare(IRFilename.length() - LLSuffix.length(), | |
| 323 LLSuffix.length(), LLSuffix) == 0) | |
| 324 BuildOnRead = false; | |
| 325 | |
| 326 // With the ELF writer, use a raw_fd_ostream to allow seeking. | |
| 327 // Also don't buffer, otherwise it gets pretty slow. | |
| 328 std::unique_ptr<Ice::Ostream> Os; | |
| 329 std::unique_ptr<Ice::ELFStreamer> ELFStr; | |
| 330 std::ofstream Ofs; | |
| 331 switch (OutFileType) { | |
| 332 case Ice::FT_Elf: { | |
| 333 if (OutputFilename == "-") { | |
| 334 *Ls << "Error: writing binary ELF to stdout is unsupported\n"; | |
| 335 return GetReturnValue(Ice::EC_Args); | |
| 336 } | |
| 337 std::string ErrorInfo; | |
| 338 raw_fd_ostream *FdOs = | |
| 339 new raw_fd_ostream(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None); | |
| 340 Os.reset(FdOs); | |
| 341 if (!ErrorInfo.empty()) { | |
| 342 *Ls << "Failed to open output file: " << OutputFilename << ":\n" | |
| 343 << ErrorInfo << "\n"; | |
| 344 return GetReturnValue(Ice::EC_Args); | |
| 345 } | |
| 346 ELFStr.reset(new Ice::ELFStreamer(*FdOs)); | |
| 347 } break; | |
| 348 case Ice::FT_Asm: | |
| 349 case Ice::FT_Iasm: { | |
| 350 if (OutputFilename != "-") { | |
| 351 Ofs.open(OutputFilename.c_str(), std::ofstream::out); | |
| 352 Os.reset(new raw_os_ostream(Ofs)); | |
| 353 } else { | |
| 354 Os.reset(new raw_os_ostream(std::cout)); | |
| 355 } | |
| 356 Os->SetUnbuffered(); | |
| 357 } break; | |
| 358 } | |
| 359 | |
| 360 Ice::GlobalContext Ctx(Ls.get(), Os.get(), ELFStr.get(), VMask, TargetArch, | |
| 361 OptLevel, TestPrefix, Flags); | |
| 362 | |
| 363 Ice::TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx); | |
| 364 | |
| 365 if (OutFileType == Ice::FT_Elf) { | |
| 366 Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx); | |
| 367 Ctx.getObjectWriter()->writeInitialELFHeader(); | |
| 368 } | |
| 369 | |
| 370 Ctx.startWorkerThreads(); | |
| 371 | |
| 372 std::unique_ptr<Ice::Translator> Translator; | |
| 373 if (BuildOnRead) { | |
| 374 std::unique_ptr<Ice::PNaClTranslator> PTranslator( | |
| 375 new Ice::PNaClTranslator(&Ctx)); | |
| 376 PTranslator->translate(IRFilename); | |
| 377 Translator.reset(PTranslator.release()); | |
| 378 } else if (ALLOW_LLVM_IR) { | |
| 379 // Parse the input LLVM IR file into a module. | |
| 380 SMDiagnostic Err; | |
| 381 Ice::TimerMarker T1(Ice::TimerStack::TT_parse, &Ctx); | |
| 382 raw_ostream *Verbose = LLVMVerboseErrors ? &errs() : nullptr; | |
| 383 Module *Mod = NaClParseIRFile(IRFilename, InputFileFormat, Err, Verbose, | |
| 384 getGlobalContext()); | |
| 385 | |
| 386 if (!Mod) { | |
| 387 Err.print(argv[0], errs()); | |
| 388 return GetReturnValue(Ice::EC_Bitcode); | |
| 389 } | |
| 390 | |
| 391 std::unique_ptr<Ice::Converter> Converter(new Ice::Converter(Mod, &Ctx)); | |
| 392 Converter->convertToIce(); | |
| 393 Translator.reset(Converter.release()); | |
| 394 } else { | |
| 395 *Ls << "Error: Build doesn't allow LLVM IR, " | |
| 396 << "--build-on-read=0 not allowed\n"; | |
| 397 return GetReturnValue(Ice::EC_Args); | |
| 398 } | |
| 399 | |
| 400 Ctx.waitForWorkerThreads(); | |
| 401 Translator->transferErrorCode(); | |
| 402 Translator->emitConstants(); | |
| 403 | |
| 404 if (OutFileType == Ice::FT_Elf) { | |
| 405 Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx); | |
| 406 Ctx.getObjectWriter()->setUndefinedSyms(Ctx.getConstantExternSyms()); | |
| 407 Ctx.getObjectWriter()->writeNonUserSections(); | |
| 408 } | |
| 409 if (SubzeroTimingEnabled) | |
| 410 Ctx.dumpTimers(); | |
| 411 if (TimeEachFunction) { | |
| 412 const bool DumpCumulative = false; | |
| 413 Ctx.dumpTimers(Ice::GlobalContext::TSK_Funcs, DumpCumulative); | |
| 414 } | |
| 415 const bool FinalStats = true; | |
| 416 Ctx.dumpStats("_FINAL_", FinalStats); | |
| 417 return GetReturnValue(Ctx.getErrorStatus()->value()); | |
| 418 } | |
| OLD | NEW |