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 |