/src/hermes/lib/Utils/Dumper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include <cctype> |
9 | | #include <string> |
10 | | |
11 | | #include "llvh/ADT/DenseSet.h" |
12 | | #include "llvh/ADT/SmallVector.h" |
13 | | #include "llvh/Support/Casting.h" |
14 | | #include "llvh/Support/ErrorHandling.h" |
15 | | #include "llvh/Support/GraphWriter.h" |
16 | | #include "llvh/Support/raw_ostream.h" |
17 | | |
18 | | #include "hermes/AST/Context.h" |
19 | | #include "hermes/FrontEndDefs/Builtins.h" |
20 | | #include "hermes/IR/CFG.h" |
21 | | #include "hermes/IR/IR.h" |
22 | | #include "hermes/IR/IRVisitor.h" |
23 | | #include "hermes/IR/Instrs.h" |
24 | | #include "hermes/Optimizer/Wasm/WasmIntrinsics.h" |
25 | | #include "hermes/Support/Statistic.h" |
26 | | #include "hermes/Utils/Dumper.h" |
27 | | |
28 | | using namespace hermes; |
29 | | |
30 | | using llvh::dyn_cast; |
31 | | using llvh::isa; |
32 | | |
33 | | namespace hermes { |
34 | | |
35 | 0 | std::string IRPrinter::escapeStr(llvh::StringRef name) { |
36 | 0 | std::string s = name.str(); |
37 | 0 | std::string out; |
38 | 0 | out += getQuoteSign(); |
39 | 0 | for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { |
40 | 0 | unsigned char c = *i; |
41 | 0 | if (std::isprint(c) && c != '\\' && c != '"') { |
42 | 0 | out += c; |
43 | 0 | } else { |
44 | 0 | out += "\\\\"; |
45 | 0 | switch (c) { |
46 | 0 | case '"': |
47 | 0 | out += "\\\""; |
48 | 0 | break; |
49 | 0 | case '\\': |
50 | 0 | out += "\\\\"; |
51 | 0 | break; |
52 | 0 | case '\t': |
53 | 0 | out += 't'; |
54 | 0 | break; |
55 | 0 | case '\r': |
56 | 0 | out += 'r'; |
57 | 0 | break; |
58 | 0 | case '\n': |
59 | 0 | out += 'n'; |
60 | 0 | break; |
61 | 0 | default: |
62 | 0 | char const *const hexdig = "0123456789ABCDEF"; |
63 | 0 | out += 'x'; |
64 | 0 | out += hexdig[c >> 4]; |
65 | 0 | out += hexdig[c & 0xF]; |
66 | 0 | } |
67 | 0 | } |
68 | 0 | } |
69 | 0 | out += getQuoteSign(); |
70 | 0 | return out; |
71 | 0 | } |
72 | | |
73 | 0 | std::string IRPrinter::quoteStr(llvh::StringRef name) { |
74 | 0 | if (name.count(" ") || name.empty()) { |
75 | 0 | return getQuoteSign() + name.str() + getQuoteSign(); |
76 | 0 | } |
77 | 0 | return name.str(); |
78 | 0 | } |
79 | | |
80 | 0 | void InstructionNamer::clear() { |
81 | 0 | Counter = 0; |
82 | 0 | InstrMap.clear(); |
83 | 0 | } |
84 | 0 | unsigned InstructionNamer::getNumber(Value *T) { |
85 | 0 | auto It = InstrMap.find(T); |
86 | 0 | if (It != InstrMap.end()) { |
87 | 0 | return It->second; |
88 | 0 | } |
89 | 0 | InstrMap[T] = Counter; |
90 | 0 | return Counter++; |
91 | 0 | } |
92 | | |
93 | 0 | void IRPrinter::printTypeLabel(Type T) { |
94 | | // We don't print type annotations for unknown types. |
95 | 0 | if (T.isAnyType()) |
96 | 0 | return; |
97 | 0 | os << " : " << T; |
98 | 0 | } |
99 | | |
100 | 0 | void IRPrinter::printValueLabel(Instruction *I, Value *V, unsigned opIndex) { |
101 | 0 | auto &ctx = I->getContext(); |
102 | 0 | if (isa<CallBuiltinInst>(I) && opIndex == CallInst::CalleeIdx) { |
103 | 0 | os << "[" |
104 | 0 | << getBuiltinMethodName(cast<CallBuiltinInst>(I)->getBuiltinIndex()) |
105 | 0 | << "]"; |
106 | | #ifdef HERMES_RUN_WASM |
107 | | } else if (isa<CallIntrinsicInst>(I) && opIndex == 0) { |
108 | | os << "[" |
109 | | << getWasmIntrinsicsName( |
110 | | cast<CallIntrinsicInst>(I)->getIntrinsicsIndex()) |
111 | | << "]"; |
112 | | #endif |
113 | 0 | } else if (isa<GetBuiltinClosureInst>(I) && opIndex == 0) { |
114 | 0 | os << "[" |
115 | 0 | << getBuiltinMethodName( |
116 | 0 | cast<GetBuiltinClosureInst>(I)->getBuiltinIndex()) |
117 | 0 | << "]"; |
118 | 0 | } else if (auto LBI = dyn_cast<LiteralBigInt>(V)) { |
119 | 0 | os << LBI->getValue()->str(); |
120 | 0 | } else if (auto LS = dyn_cast<LiteralString>(V)) { |
121 | 0 | os << escapeStr(ctx.toString(LS->getValue())); |
122 | 0 | } else if (auto LB = dyn_cast<LiteralBool>(V)) { |
123 | 0 | os << (LB->getValue() ? "true" : "false"); |
124 | 0 | } else if (auto LN = dyn_cast<LiteralNumber>(V)) { |
125 | 0 | const auto Num = LN->getValue(); |
126 | 0 | if (Num == 0 && std::signbit(Num)) { |
127 | | // Ensure we output -0 correctly |
128 | 0 | os << "-0"; |
129 | 0 | } else { |
130 | 0 | char buf[NUMBER_TO_STRING_BUF_SIZE]; |
131 | 0 | numberToString(LN->getValue(), buf, sizeof(buf)); |
132 | 0 | os << buf; |
133 | 0 | } |
134 | 0 | } else if (isa<LiteralEmpty>(V)) { |
135 | 0 | os << "empty"; |
136 | 0 | } else if (isa<LiteralNull>(V)) { |
137 | 0 | os << "null"; |
138 | 0 | } else if (isa<LiteralUndefined>(V)) { |
139 | 0 | os << "undefined"; |
140 | 0 | } else if (isa<GlobalObject>(V)) { |
141 | 0 | os << "globalObject"; |
142 | 0 | } else if (isa<EmptySentinel>(V)) { |
143 | 0 | os << "empty"; |
144 | 0 | } else if (isa<Instruction>(V)) { |
145 | 0 | os << "%" << InstNamer.getNumber(V); |
146 | 0 | } else if (isa<BasicBlock>(V)) { |
147 | 0 | os << "%BB" << BBNamer.getNumber(V); |
148 | 0 | } else if (auto L = dyn_cast<Label>(V)) { |
149 | 0 | auto Name = L->get(); |
150 | 0 | os << "$" << quoteStr(ctx.toString(Name)); |
151 | 0 | } else if (auto P = dyn_cast<Parameter>(V)) { |
152 | 0 | auto Name = P->getName(); |
153 | 0 | os << "%" << ctx.toString(Name); |
154 | 0 | } else if (auto F = dyn_cast<Function>(V)) { |
155 | 0 | os << "%"; |
156 | 0 | printFunctionName(F, PrintFunctionParams::No); |
157 | 0 | } else if (auto S = dyn_cast<ScopeDesc>(V)) { |
158 | 0 | os << "%"; |
159 | 0 | printScopeLabel(S); |
160 | 0 | } else if (auto VR = dyn_cast<Variable>(V)) { |
161 | 0 | os << "["; |
162 | 0 | printVariableName(VR); |
163 | 0 | if (I->getParent()->getParent() != VR->getParent()->getFunction()) { |
164 | 0 | llvh::StringRef scopeName = |
165 | 0 | VR->getParent()->getFunction()->getInternalNameStr(); |
166 | 0 | os << "@" << quoteStr(scopeName); |
167 | 0 | } |
168 | 0 | os << "]"; |
169 | 0 | } else { |
170 | 0 | llvm_unreachable("Invalid value"); |
171 | 0 | } |
172 | | |
173 | 0 | printTypeLabel(V->getType()); |
174 | 0 | } |
175 | | |
176 | 0 | void IRPrinter::printFunctionHeader(Function *F) { |
177 | 0 | std::string defKindStr = F->getDefinitionKindStr(false); |
178 | |
|
179 | 0 | os << defKindStr << " "; |
180 | 0 | printFunctionName(F, PrintFunctionParams::Yes); |
181 | 0 | printTypeLabel(F->getType()); |
182 | 0 | } |
183 | | |
184 | 0 | void IRPrinter::printFunctionVariables(Function *F) { |
185 | 0 | bool hasGlobals = false; |
186 | 0 | if (F->isGlobalScope()) { |
187 | 0 | auto &Ctx = F->getContext(); |
188 | 0 | bool first2 = true; |
189 | 0 | for (auto *GP : F->getParent()->getGlobalProperties()) { |
190 | 0 | if (!GP->isDeclared()) |
191 | 0 | continue; |
192 | 0 | if (first2) { |
193 | 0 | hasGlobals = true; |
194 | 0 | os << "globals = ["; |
195 | 0 | } else { |
196 | 0 | os << ", "; |
197 | 0 | } |
198 | 0 | os << Ctx.toString(GP->getName()->getValue()); |
199 | 0 | first2 = false; |
200 | 0 | } |
201 | 0 | if (!first2) |
202 | 0 | os << "]"; |
203 | 0 | } |
204 | |
|
205 | 0 | bool printNewLine = hasGlobals; |
206 | 0 | F->forEachScope([&](ScopeDesc *S) { |
207 | 0 | if (printNewLine) { |
208 | 0 | os << "\n"; |
209 | 0 | } |
210 | 0 | printNewLine = true; |
211 | 0 | printScopeLabel(S); |
212 | 0 | os << " = ["; |
213 | 0 | bool first = true; |
214 | 0 | for (auto V : S->getVariables()) { |
215 | 0 | if (!first) { |
216 | 0 | os << ", "; |
217 | 0 | } |
218 | 0 | printVariableName(V); |
219 | 0 | printTypeLabel(V->getType()); |
220 | 0 | first = false; |
221 | 0 | } |
222 | 0 | os << "]"; |
223 | 0 | }); |
224 | 0 | } |
225 | | |
226 | 0 | void IRPrinter::printInstructionDestination(Instruction *I) { |
227 | 0 | os << "%" << InstNamer.getNumber(I); |
228 | 0 | } |
229 | | |
230 | 0 | void IRPrinter::printInstruction(Instruction *I) { |
231 | 0 | printInstructionDestination(I); |
232 | 0 | os << " = "; |
233 | 0 | os << I->getName(); |
234 | |
|
235 | 0 | bool first = true; |
236 | |
|
237 | 0 | if (auto *binop = dyn_cast<BinaryOperatorInst>(I)) { |
238 | 0 | os << " '" << binop->getOperatorStr() << "'"; |
239 | 0 | first = false; |
240 | 0 | } else if (auto *cmpbr = dyn_cast<CompareBranchInst>(I)) { |
241 | 0 | os << " '" << cmpbr->getOperatorStr() << "'"; |
242 | 0 | first = false; |
243 | 0 | } else if (auto *unop = dyn_cast<UnaryOperatorInst>(I)) { |
244 | 0 | os << " '" << unop->getOperatorStr() << "'"; |
245 | 0 | first = false; |
246 | 0 | } |
247 | |
|
248 | 0 | for (int i = 0, e = I->getNumOperands(); i < e; i++) { |
249 | 0 | Value *O = I->getOperand(i); |
250 | 0 | os << (first ? " " : ", "); |
251 | 0 | printValueLabel(I, O, i); |
252 | 0 | first = false; |
253 | 0 | } |
254 | |
|
255 | 0 | auto codeGenOpts = I->getContext().getCodeGenerationSettings(); |
256 | 0 | const char *prefix = " // "; |
257 | |
|
258 | 0 | if (codeGenOpts.dumpTextifiedCallee) { |
259 | 0 | auto &ctx = I->getParent()->getParent()->getContext(); |
260 | 0 | if (auto call = llvh::dyn_cast<CallInst>(I)) { |
261 | 0 | if (LiteralString *textifiedCallee = call->getTextifiedCallee()) { |
262 | 0 | os << prefix << "textified callee: " |
263 | 0 | << escapeStr(ctx.toString(textifiedCallee->getValue())); |
264 | 0 | prefix = ", "; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 0 | if (codeGenOpts.dumpSourceLevelScope) { |
269 | 0 | if (auto *originalScope = I->getSourceLevelScope()) { |
270 | 0 | os << prefix << "scope: "; |
271 | 0 | printScopeLabel(originalScope); |
272 | 0 | prefix = ", "; |
273 | 0 | } |
274 | 0 | } |
275 | | |
276 | | // Print the use list if there is any user for the instruction. |
277 | 0 | if (!codeGenOpts.dumpUseList || I->getUsers().empty()) |
278 | 0 | return; |
279 | | |
280 | 0 | llvh::DenseSet<Instruction *> Visited; |
281 | 0 | os << prefix << "users:"; |
282 | 0 | for (auto &U : I->getUsers()) { |
283 | 0 | auto *II = cast<Instruction>(U); |
284 | 0 | assert(II && "Expecting user to be an Instruction"); |
285 | 0 | if (Visited.find(II) != Visited.end()) |
286 | 0 | continue; |
287 | 0 | Visited.insert(II); |
288 | 0 | os << " %" << InstNamer.getNumber(II); |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | 0 | void IRPrinter::printSourceLocation(SMLoc loc) { |
293 | 0 | SourceErrorManager::SourceCoords coords; |
294 | 0 | if (!sm_.findBufferLineAndLoc(loc, coords)) |
295 | 0 | return; |
296 | | |
297 | 0 | os << sm_.getSourceUrl(coords.bufId) << ":" << coords.line << ":" |
298 | 0 | << coords.col; |
299 | 0 | } |
300 | | |
301 | 0 | void IRPrinter::printSourceLocation(SMRange rng) { |
302 | 0 | SourceErrorManager::SourceCoords start, end; |
303 | 0 | if (!sm_.findBufferLineAndLoc(rng.Start, start) || |
304 | 0 | !sm_.findBufferLineAndLoc(rng.End, end)) |
305 | 0 | return; |
306 | | |
307 | 0 | os << "[" << sm_.getSourceUrl(start.bufId) << ":" << start.line << ":" |
308 | 0 | << start.col << " ... " << sm_.getSourceUrl(end.bufId) << ":" << end.line |
309 | 0 | << ":" << end.col << ")"; |
310 | 0 | } |
311 | | |
312 | 0 | void IRPrinter::printScopeLabel(ScopeDesc *S) { |
313 | 0 | os << "S{"; |
314 | 0 | printFunctionName(S->getFunction(), PrintFunctionParams::No); |
315 | 0 | printScopeRange(S, S->getFunction()->getFunctionScopeDesc()); |
316 | 0 | os << "}"; |
317 | 0 | } |
318 | | |
319 | 0 | void IRPrinter::printScope(ScopeDesc *S) { |
320 | 0 | os << "#" << ScopeNamer.getNumber(S); |
321 | 0 | } |
322 | | |
323 | 0 | void IRPrinter::printScopeRange(ScopeDesc *Start, ScopeDesc *End) { |
324 | 0 | if (Start != End) { |
325 | 0 | printScopeRange(Start->getParent(), End); |
326 | 0 | printScope(Start); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | 0 | void IRPrinter::printScopeChain(ScopeDesc *S) { |
331 | 0 | if (S && S->getParent()) { |
332 | 0 | printScope(S->getParent()); |
333 | 0 | } |
334 | 0 | printScope(S); |
335 | 0 | } |
336 | | |
337 | | void IRPrinter::printFunctionName( |
338 | | Function *F, |
339 | 0 | PrintFunctionParams printFunctionParams) { |
340 | 0 | auto &ctx = F->getContext(); |
341 | 0 | os << quoteStr(ctx.toString(F->getInternalName())); |
342 | 0 | printScopeChain(F->getFunctionScopeDesc()->getParent()); |
343 | 0 | os << "("; |
344 | 0 | if (printFunctionParams != PrintFunctionParams::No) { |
345 | 0 | bool first = true; |
346 | 0 | for (auto P : F->getParameters()) { |
347 | 0 | if (!first) { |
348 | 0 | os << ", "; |
349 | 0 | } |
350 | 0 | os << ctx.toString(P->getName()); |
351 | 0 | printTypeLabel(P->getType()); |
352 | 0 | first = false; |
353 | 0 | } |
354 | 0 | } |
355 | 0 | os << ")"; |
356 | 0 | printScope(F->getFunctionScopeDesc()); |
357 | 0 | } |
358 | | |
359 | 0 | void IRPrinter::printVariableName(Variable *V) { |
360 | 0 | ScopeDesc *VS = V->getParent(); |
361 | 0 | auto &ctx = VS->getFunction()->getContext(); |
362 | 0 | os << ctx.toString(V->getName()); |
363 | 0 | printScope(VS); |
364 | 0 | } |
365 | | |
366 | 0 | void IRPrinter::visitModule(const Module &M) { |
367 | 0 | ScopeNamer.clear(); |
368 | 0 | visitScope(*M.getInitialScope()); |
369 | | |
370 | | // Use IRVisitor dispatch to visit each individual function. |
371 | 0 | for (auto &F : M) |
372 | 0 | visit(F); |
373 | 0 | } |
374 | | |
375 | 0 | void IRPrinter::visitScope(const ScopeDesc &S) { |
376 | 0 | ScopeNamer.getNumber(const_cast<ScopeDesc *>(&S)); |
377 | 0 | for (ScopeDesc *inner : S.getInnerScopes()) { |
378 | 0 | visitScope(*inner); |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | 0 | void IRPrinter::visitFunction(const Function &F) { |
383 | 0 | auto *UF = const_cast<Function *>(&F); |
384 | 0 | os.indent(Indent); |
385 | 0 | BBNamer.clear(); |
386 | 0 | InstNamer.clear(); |
387 | | // Number all instructions sequentially. |
388 | 0 | for (auto &BB : *UF) |
389 | 0 | for (auto &I : BB) { |
390 | 0 | InstNamer.getNumber(&I); |
391 | 0 | } |
392 | |
|
393 | 0 | printFunctionHeader(UF); |
394 | 0 | os << "\n"; |
395 | 0 | printFunctionVariables(UF); |
396 | 0 | os << "\n"; |
397 | |
|
398 | 0 | auto codeGenOpts = F.getContext().getCodeGenerationSettings(); |
399 | 0 | if (codeGenOpts.dumpSourceLocation) { |
400 | 0 | os << "source location: "; |
401 | 0 | printSourceLocation(F.getSourceRange()); |
402 | 0 | os << "\n"; |
403 | 0 | } |
404 | | |
405 | | // Use IRVisitor dispatch to visit the basic blocks. |
406 | 0 | for (auto &BB : F) { |
407 | 0 | visit(BB); |
408 | 0 | } |
409 | |
|
410 | 0 | os.indent(Indent); |
411 | 0 | os << "function_end" << "\n"; |
412 | 0 | os << "\n"; |
413 | 0 | } |
414 | | |
415 | 0 | void IRPrinter::visitBasicBlock(const BasicBlock &BB) { |
416 | 0 | auto *UBB = const_cast<BasicBlock *>(&BB); |
417 | 0 | os.indent(Indent); |
418 | 0 | os << "%BB" << BBNamer.getNumber(UBB) << ":\n"; |
419 | 0 | Indent += 2; |
420 | | |
421 | | // Use IRVisitor dispatch to visit the instructions. |
422 | 0 | for (auto &I : BB) { |
423 | 0 | visit(I); |
424 | 0 | } |
425 | |
|
426 | 0 | Indent -= 2; |
427 | 0 | } |
428 | | |
429 | 0 | void IRPrinter::visitInstruction(const Instruction &I) { |
430 | 0 | auto *UII = const_cast<Instruction *>(&I); |
431 | 0 | auto codeGenOpts = I.getContext().getCodeGenerationSettings(); |
432 | 0 | if (codeGenOpts.dumpSourceLocation) { |
433 | 0 | os << "; "; |
434 | 0 | printSourceLocation(UII->getLocation()); |
435 | 0 | os << "\n"; |
436 | 0 | } |
437 | 0 | os.indent(Indent); |
438 | 0 | printInstruction(UII); |
439 | 0 | os << "\n"; |
440 | 0 | } |
441 | | |
442 | | } // namespace hermes |
443 | | |
444 | | namespace { |
445 | | |
446 | | /// This class prints Functions into dotty graphs. This struct inherits the |
447 | | /// IRVisitor and reimplement the visitFunction and visitBasicBlock function. |
448 | | struct DottyPrinter : public IRVisitor<DottyPrinter, void> { |
449 | | llvh::raw_ostream &os; |
450 | | llvh::SmallVector<std::pair<std::string, std::string>, 4> Edges; |
451 | | IRPrinter Printer; |
452 | | |
453 | | explicit DottyPrinter( |
454 | | Context &ctx, |
455 | | llvh::raw_ostream &ost, |
456 | | llvh::StringRef Title) |
457 | 0 | : os(ost), Printer(ctx, ost, /* escape output */ true) { |
458 | 0 | os << " digraph g {\n graph [ rankdir = \"TD\" ];\n"; |
459 | 0 | os << "labelloc=\"t\"; "; |
460 | 0 | os << " node [ fontsize = \"16\" shape = \"record\" ]; edge [ ];\n"; |
461 | 0 | } |
462 | | |
463 | 0 | ~DottyPrinter() { |
464 | 0 | os << "\n"; |
465 | | |
466 | | // Print the edges between the blocks. |
467 | 0 | for (auto T : Edges) { |
468 | 0 | os << "" << T.first << " ->"; |
469 | 0 | os << "" << T.second << ";\n"; |
470 | 0 | } |
471 | 0 | os << "\n}\n"; |
472 | 0 | } |
473 | | |
474 | | /// Convert pointers into unique textual ids. |
475 | 0 | static std::string toString(BasicBlock *ptr) { |
476 | 0 | auto Num = (size_t)ptr; |
477 | 0 | return std::to_string(Num); |
478 | 0 | } |
479 | | |
480 | | /// Reimplement the visitFunction in IRVisitor. |
481 | 0 | void visitFunction(const Function &F) { |
482 | 0 | os << "label=\""; |
483 | 0 | Printer.printFunctionHeader(const_cast<Function *>(&F)); |
484 | 0 | os << "\";\n"; |
485 | | |
486 | | // Pre-assign every instruction a number, by doing this we avoid |
487 | | // assigning non-consecutive numbers to consecutive instructions if we |
488 | | // are dumping user list. Its also not a bad practice to initialize the |
489 | | // InstNamer table before we dump all the instructions. |
490 | 0 | for (auto &BB : F) { |
491 | 0 | for (auto &II : BB) { |
492 | 0 | (void)Printer.InstNamer.getNumber(const_cast<Instruction *>(&II)); |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | // Use IRVisitor dispatch to visit the basic blocks. |
497 | 0 | for (auto &BB : F) { |
498 | 0 | visit(BB); |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | | /// Reimplement the visitBasicBlock in the IRVisitor. |
503 | | /// Visit all of the basic blocks in the program and render them into dotty |
504 | | /// records. Save edges between basic blocks and print them when we finish |
505 | | /// visiting all of the blocks in the program. |
506 | 0 | void visitBasicBlock(const BasicBlock &V) { |
507 | 0 | auto *BB = const_cast<BasicBlock *>(&V); |
508 | 0 | os << "\"" << toString(BB) << "\""; |
509 | 0 | os << "[ label = \"{ "; |
510 | |
|
511 | 0 | os << " { <self> | <head> \\<\\<%BB" << Printer.BBNamer.getNumber(BB) |
512 | 0 | << "\\>\\> | } "; |
513 | |
|
514 | 0 | int counter = 0; |
515 | | |
516 | | // For each instruction in the basic block: |
517 | 0 | for (auto &I : *BB) { |
518 | 0 | counter++; |
519 | 0 | os << " | "; |
520 | | |
521 | | // Print the instruction. |
522 | 0 | os << "<L" << counter << ">"; |
523 | 0 | Printer.printInstruction(&I); |
524 | 0 | } |
525 | |
|
526 | 0 | for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) { |
527 | 0 | auto From = toString(BB) + ":L" + std::to_string(counter); |
528 | 0 | Edges.push_back({From, toString(*I) + (*I == BB ? ":self" : ":head")}); |
529 | 0 | } |
530 | | |
531 | | // Emit the record: |
532 | 0 | os << "}\" shape = \"record\" ]; \n"; |
533 | 0 | } |
534 | | }; |
535 | | |
536 | | } // anonymous namespace |
537 | | |
538 | 0 | void hermes::viewGraph(Function *F) { |
539 | 0 | #ifndef NDEBUG |
540 | 0 | auto &Ctx = F->getContext(); |
541 | 0 | llvh::StringRef Name = Ctx.toString(F->getInternalName()); |
542 | 0 | int FD; |
543 | | // Windows can't always handle long paths, so limit the length of the name. |
544 | 0 | std::string N = Name.str(); |
545 | 0 | N = N.substr(0, std::min<std::size_t>(N.size(), 140)); |
546 | 0 | std::string Filename = llvh::createGraphFilename(N, FD); |
547 | 0 | { |
548 | 0 | llvh::raw_fd_ostream O(FD, /*shouldClose=*/true); |
549 | |
|
550 | 0 | if (FD == -1) { |
551 | 0 | llvh::errs() << "error opening file '" << Filename << "' for writing!\n"; |
552 | 0 | return; |
553 | 0 | } |
554 | | |
555 | 0 | DottyPrinter D(F->getContext(), O, Name); |
556 | 0 | D.visitFunction(*F); |
557 | 0 | } |
558 | | |
559 | 0 | llvh::DisplayGraph(Filename); |
560 | 0 | llvh::errs() << " done. \n"; |
561 | 0 | #endif |
562 | 0 | } |