1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "graph_visualizer.h"
18
19 #include <dlfcn.h>
20
21 #include <cctype>
22 #include <sstream>
23
24 #include "bounds_check_elimination.h"
25 #include "builder.h"
26 #include "code_generator.h"
27 #include "dead_code_elimination.h"
28 #include "disassembler.h"
29 #include "inliner.h"
30 #include "licm.h"
31 #include "nodes.h"
32 #include "optimization.h"
33 #include "reference_type_propagation.h"
34 #include "register_allocator.h"
35 #include "ssa_liveness_analysis.h"
36 #include "utils/assembler.h"
37
38 namespace art {
39
HasWhitespace(const char * str)40 static bool HasWhitespace(const char* str) {
41 DCHECK(str != nullptr);
42 while (str[0] != 0) {
43 if (isspace(str[0])) {
44 return true;
45 }
46 str++;
47 }
48 return false;
49 }
50
51 class StringList {
52 public:
53 enum Format {
54 kArrayBrackets,
55 kSetBrackets,
56 };
57
58 // Create an empty list
StringList(Format format=kArrayBrackets)59 explicit StringList(Format format = kArrayBrackets) : format_(format), is_empty_(true) {}
60
61 // Construct StringList from a linked list. List element class T
62 // must provide methods `GetNext` and `Dump`.
63 template<class T>
StringList(T * first_entry,Format format=kArrayBrackets)64 explicit StringList(T* first_entry, Format format = kArrayBrackets) : StringList(format) {
65 for (T* current = first_entry; current != nullptr; current = current->GetNext()) {
66 current->Dump(NewEntryStream());
67 }
68 }
69
NewEntryStream()70 std::ostream& NewEntryStream() {
71 if (is_empty_) {
72 is_empty_ = false;
73 } else {
74 sstream_ << ",";
75 }
76 return sstream_;
77 }
78
79 private:
80 Format format_;
81 bool is_empty_;
82 std::ostringstream sstream_;
83
84 friend std::ostream& operator<<(std::ostream& os, const StringList& list);
85 };
86
operator <<(std::ostream & os,const StringList & list)87 std::ostream& operator<<(std::ostream& os, const StringList& list) {
88 switch (list.format_) {
89 case StringList::kArrayBrackets: return os << "[" << list.sstream_.str() << "]";
90 case StringList::kSetBrackets: return os << "{" << list.sstream_.str() << "}";
91 default:
92 LOG(FATAL) << "Invalid StringList format";
93 UNREACHABLE();
94 }
95 }
96
97 typedef Disassembler* create_disasm_prototype(InstructionSet instruction_set,
98 DisassemblerOptions* options);
99 class HGraphVisualizerDisassembler {
100 public:
HGraphVisualizerDisassembler(InstructionSet instruction_set,const uint8_t * base_address,const uint8_t * end_address)101 HGraphVisualizerDisassembler(InstructionSet instruction_set,
102 const uint8_t* base_address,
103 const uint8_t* end_address)
104 : instruction_set_(instruction_set), disassembler_(nullptr) {
105 libart_disassembler_handle_ =
106 dlopen(kIsDebugBuild ? "libartd-disassembler.so" : "libart-disassembler.so", RTLD_NOW);
107 if (libart_disassembler_handle_ == nullptr) {
108 LOG(WARNING) << "Failed to dlopen libart-disassembler: " << dlerror();
109 return;
110 }
111 create_disasm_prototype* create_disassembler = reinterpret_cast<create_disasm_prototype*>(
112 dlsym(libart_disassembler_handle_, "create_disassembler"));
113 if (create_disassembler == nullptr) {
114 LOG(WARNING) << "Could not find create_disassembler entry: " << dlerror();
115 return;
116 }
117 // Reading the disassembly from 0x0 is easier, so we print relative
118 // addresses. We will only disassemble the code once everything has
119 // been generated, so we can read data in literal pools.
120 disassembler_ = std::unique_ptr<Disassembler>((*create_disassembler)(
121 instruction_set,
122 new DisassemblerOptions(/* absolute_addresses */ false,
123 base_address,
124 end_address,
125 /* can_read_literals */ true)));
126 }
127
~HGraphVisualizerDisassembler()128 ~HGraphVisualizerDisassembler() {
129 // We need to call ~Disassembler() before we close the library.
130 disassembler_.reset();
131 if (libart_disassembler_handle_ != nullptr) {
132 dlclose(libart_disassembler_handle_);
133 }
134 }
135
Disassemble(std::ostream & output,size_t start,size_t end) const136 void Disassemble(std::ostream& output, size_t start, size_t end) const {
137 if (disassembler_ == nullptr) {
138 return;
139 }
140
141 const uint8_t* base = disassembler_->GetDisassemblerOptions()->base_address_;
142 if (instruction_set_ == kThumb2) {
143 // ARM and Thumb-2 use the same disassembler. The bottom bit of the
144 // address is used to distinguish between the two.
145 base += 1;
146 }
147 disassembler_->Dump(output, base + start, base + end);
148 }
149
150 private:
151 InstructionSet instruction_set_;
152 std::unique_ptr<Disassembler> disassembler_;
153
154 void* libart_disassembler_handle_;
155 };
156
157
158 /**
159 * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra.
160 */
161 class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
162 public:
HGraphVisualizerPrinter(HGraph * graph,std::ostream & output,const char * pass_name,bool is_after_pass,bool graph_in_bad_state,const CodeGenerator & codegen,const DisassemblyInformation * disasm_info=nullptr)163 HGraphVisualizerPrinter(HGraph* graph,
164 std::ostream& output,
165 const char* pass_name,
166 bool is_after_pass,
167 bool graph_in_bad_state,
168 const CodeGenerator& codegen,
169 const DisassemblyInformation* disasm_info = nullptr)
170 : HGraphDelegateVisitor(graph),
171 output_(output),
172 pass_name_(pass_name),
173 is_after_pass_(is_after_pass),
174 graph_in_bad_state_(graph_in_bad_state),
175 codegen_(codegen),
176 disasm_info_(disasm_info),
177 disassembler_(disasm_info_ != nullptr
178 ? new HGraphVisualizerDisassembler(
179 codegen_.GetInstructionSet(),
180 codegen_.GetAssembler().CodeBufferBaseAddress(),
181 codegen_.GetAssembler().CodeBufferBaseAddress()
182 + codegen_.GetAssembler().CodeSize())
183 : nullptr),
184 indent_(0) {}
185
Flush()186 void Flush() {
187 // We use "\n" instead of std::endl to avoid implicit flushing which
188 // generates too many syscalls during debug-GC tests (b/27826765).
189 output_ << std::flush;
190 }
191
StartTag(const char * name)192 void StartTag(const char* name) {
193 AddIndent();
194 output_ << "begin_" << name << "\n";
195 indent_++;
196 }
197
EndTag(const char * name)198 void EndTag(const char* name) {
199 indent_--;
200 AddIndent();
201 output_ << "end_" << name << "\n";
202 }
203
PrintProperty(const char * name,const char * property)204 void PrintProperty(const char* name, const char* property) {
205 AddIndent();
206 output_ << name << " \"" << property << "\"\n";
207 }
208
PrintProperty(const char * name,const char * property,int id)209 void PrintProperty(const char* name, const char* property, int id) {
210 AddIndent();
211 output_ << name << " \"" << property << id << "\"\n";
212 }
213
PrintEmptyProperty(const char * name)214 void PrintEmptyProperty(const char* name) {
215 AddIndent();
216 output_ << name << "\n";
217 }
218
PrintTime(const char * name)219 void PrintTime(const char* name) {
220 AddIndent();
221 output_ << name << " " << time(nullptr) << "\n";
222 }
223
PrintInt(const char * name,int value)224 void PrintInt(const char* name, int value) {
225 AddIndent();
226 output_ << name << " " << value << "\n";
227 }
228
AddIndent()229 void AddIndent() {
230 for (size_t i = 0; i < indent_; ++i) {
231 output_ << " ";
232 }
233 }
234
GetTypeId(Primitive::Type type)235 char GetTypeId(Primitive::Type type) {
236 // Note that Primitive::Descriptor would not work for us
237 // because it does not handle reference types (that is kPrimNot).
238 switch (type) {
239 case Primitive::kPrimBoolean: return 'z';
240 case Primitive::kPrimByte: return 'b';
241 case Primitive::kPrimChar: return 'c';
242 case Primitive::kPrimShort: return 's';
243 case Primitive::kPrimInt: return 'i';
244 case Primitive::kPrimLong: return 'j';
245 case Primitive::kPrimFloat: return 'f';
246 case Primitive::kPrimDouble: return 'd';
247 case Primitive::kPrimNot: return 'l';
248 case Primitive::kPrimVoid: return 'v';
249 }
250 LOG(FATAL) << "Unreachable";
251 return 'v';
252 }
253
PrintPredecessors(HBasicBlock * block)254 void PrintPredecessors(HBasicBlock* block) {
255 AddIndent();
256 output_ << "predecessors";
257 for (HBasicBlock* predecessor : block->GetPredecessors()) {
258 output_ << " \"B" << predecessor->GetBlockId() << "\" ";
259 }
260 if (block->IsEntryBlock() && (disasm_info_ != nullptr)) {
261 output_ << " \"" << kDisassemblyBlockFrameEntry << "\" ";
262 }
263 output_<< "\n";
264 }
265
PrintSuccessors(HBasicBlock * block)266 void PrintSuccessors(HBasicBlock* block) {
267 AddIndent();
268 output_ << "successors";
269 for (HBasicBlock* successor : block->GetNormalSuccessors()) {
270 output_ << " \"B" << successor->GetBlockId() << "\" ";
271 }
272 output_<< "\n";
273 }
274
PrintExceptionHandlers(HBasicBlock * block)275 void PrintExceptionHandlers(HBasicBlock* block) {
276 AddIndent();
277 output_ << "xhandlers";
278 for (HBasicBlock* handler : block->GetExceptionalSuccessors()) {
279 output_ << " \"B" << handler->GetBlockId() << "\" ";
280 }
281 if (block->IsExitBlock() &&
282 (disasm_info_ != nullptr) &&
283 !disasm_info_->GetSlowPathIntervals().empty()) {
284 output_ << " \"" << kDisassemblyBlockSlowPaths << "\" ";
285 }
286 output_<< "\n";
287 }
288
DumpLocation(std::ostream & stream,const Location & location)289 void DumpLocation(std::ostream& stream, const Location& location) {
290 if (location.IsRegister()) {
291 codegen_.DumpCoreRegister(stream, location.reg());
292 } else if (location.IsFpuRegister()) {
293 codegen_.DumpFloatingPointRegister(stream, location.reg());
294 } else if (location.IsConstant()) {
295 stream << "#";
296 HConstant* constant = location.GetConstant();
297 if (constant->IsIntConstant()) {
298 stream << constant->AsIntConstant()->GetValue();
299 } else if (constant->IsLongConstant()) {
300 stream << constant->AsLongConstant()->GetValue();
301 }
302 } else if (location.IsInvalid()) {
303 stream << "invalid";
304 } else if (location.IsStackSlot()) {
305 stream << location.GetStackIndex() << "(sp)";
306 } else if (location.IsFpuRegisterPair()) {
307 codegen_.DumpFloatingPointRegister(stream, location.low());
308 stream << "|";
309 codegen_.DumpFloatingPointRegister(stream, location.high());
310 } else if (location.IsRegisterPair()) {
311 codegen_.DumpCoreRegister(stream, location.low());
312 stream << "|";
313 codegen_.DumpCoreRegister(stream, location.high());
314 } else if (location.IsUnallocated()) {
315 stream << "unallocated";
316 } else {
317 DCHECK(location.IsDoubleStackSlot());
318 stream << "2x" << location.GetStackIndex() << "(sp)";
319 }
320 }
321
StartAttributeStream(const char * name=nullptr)322 std::ostream& StartAttributeStream(const char* name = nullptr) {
323 if (name == nullptr) {
324 output_ << " ";
325 } else {
326 DCHECK(!HasWhitespace(name)) << "Checker does not allow spaces in attributes";
327 output_ << " " << name << ":";
328 }
329 return output_;
330 }
331
VisitParallelMove(HParallelMove * instruction)332 void VisitParallelMove(HParallelMove* instruction) OVERRIDE {
333 StartAttributeStream("liveness") << instruction->GetLifetimePosition();
334 StringList moves;
335 for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
336 MoveOperands* move = instruction->MoveOperandsAt(i);
337 std::ostream& str = moves.NewEntryStream();
338 DumpLocation(str, move->GetSource());
339 str << "->";
340 DumpLocation(str, move->GetDestination());
341 }
342 StartAttributeStream("moves") << moves;
343 }
344
VisitIntConstant(HIntConstant * instruction)345 void VisitIntConstant(HIntConstant* instruction) OVERRIDE {
346 StartAttributeStream() << instruction->GetValue();
347 }
348
VisitLongConstant(HLongConstant * instruction)349 void VisitLongConstant(HLongConstant* instruction) OVERRIDE {
350 StartAttributeStream() << instruction->GetValue();
351 }
352
VisitFloatConstant(HFloatConstant * instruction)353 void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE {
354 StartAttributeStream() << instruction->GetValue();
355 }
356
VisitDoubleConstant(HDoubleConstant * instruction)357 void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE {
358 StartAttributeStream() << instruction->GetValue();
359 }
360
VisitPhi(HPhi * phi)361 void VisitPhi(HPhi* phi) OVERRIDE {
362 StartAttributeStream("reg") << phi->GetRegNumber();
363 StartAttributeStream("is_catch_phi") << std::boolalpha << phi->IsCatchPhi() << std::noboolalpha;
364 }
365
VisitMemoryBarrier(HMemoryBarrier * barrier)366 void VisitMemoryBarrier(HMemoryBarrier* barrier) OVERRIDE {
367 StartAttributeStream("kind") << barrier->GetBarrierKind();
368 }
369
VisitMonitorOperation(HMonitorOperation * monitor)370 void VisitMonitorOperation(HMonitorOperation* monitor) OVERRIDE {
371 StartAttributeStream("kind") << (monitor->IsEnter() ? "enter" : "exit");
372 }
373
VisitLoadClass(HLoadClass * load_class)374 void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
375 StartAttributeStream("gen_clinit_check") << std::boolalpha
376 << load_class->MustGenerateClinitCheck() << std::noboolalpha;
377 StartAttributeStream("needs_access_check") << std::boolalpha
378 << load_class->NeedsAccessCheck() << std::noboolalpha;
379 }
380
VisitLoadString(HLoadString * load_string)381 void VisitLoadString(HLoadString* load_string) OVERRIDE {
382 StartAttributeStream("load_kind") << load_string->GetLoadKind();
383 }
384
VisitCheckCast(HCheckCast * check_cast)385 void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
386 StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
387 StartAttributeStream("must_do_null_check") << std::boolalpha
388 << check_cast->MustDoNullCheck() << std::noboolalpha;
389 }
390
VisitInstanceOf(HInstanceOf * instance_of)391 void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
392 StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind();
393 StartAttributeStream("must_do_null_check") << std::boolalpha
394 << instance_of->MustDoNullCheck() << std::noboolalpha;
395 }
396
VisitArraySet(HArraySet * array_set)397 void VisitArraySet(HArraySet* array_set) OVERRIDE {
398 StartAttributeStream("value_can_be_null") << std::boolalpha
399 << array_set->GetValueCanBeNull() << std::noboolalpha;
400 StartAttributeStream("needs_type_check") << std::boolalpha
401 << array_set->NeedsTypeCheck() << std::noboolalpha;
402 }
403
VisitCompare(HCompare * compare)404 void VisitCompare(HCompare* compare) OVERRIDE {
405 ComparisonBias bias = compare->GetBias();
406 StartAttributeStream("bias") << (bias == ComparisonBias::kGtBias
407 ? "gt"
408 : (bias == ComparisonBias::kLtBias ? "lt" : "none"));
409 }
410
VisitInvoke(HInvoke * invoke)411 void VisitInvoke(HInvoke* invoke) OVERRIDE {
412 StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex();
413 StartAttributeStream("method_name") << PrettyMethod(
414 invoke->GetDexMethodIndex(), GetGraph()->GetDexFile(), /* with_signature */ false);
415 }
416
VisitInvokeUnresolved(HInvokeUnresolved * invoke)417 void VisitInvokeUnresolved(HInvokeUnresolved* invoke) OVERRIDE {
418 VisitInvoke(invoke);
419 StartAttributeStream("invoke_type") << invoke->GetOriginalInvokeType();
420 }
421
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)422 void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
423 VisitInvoke(invoke);
424 StartAttributeStream("method_load_kind") << invoke->GetMethodLoadKind();
425 StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
426 if (invoke->IsStatic()) {
427 StartAttributeStream("clinit_check") << invoke->GetClinitCheckRequirement();
428 }
429 }
430
VisitInvokeVirtual(HInvokeVirtual * invoke)431 void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE {
432 VisitInvoke(invoke);
433 StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
434 }
435
VisitInstanceFieldGet(HInstanceFieldGet * iget)436 void VisitInstanceFieldGet(HInstanceFieldGet* iget) OVERRIDE {
437 StartAttributeStream("field_name") << PrettyField(iget->GetFieldInfo().GetFieldIndex(),
438 iget->GetFieldInfo().GetDexFile(),
439 /* with type */ false);
440 StartAttributeStream("field_type") << iget->GetFieldType();
441 }
442
VisitInstanceFieldSet(HInstanceFieldSet * iset)443 void VisitInstanceFieldSet(HInstanceFieldSet* iset) OVERRIDE {
444 StartAttributeStream("field_name") << PrettyField(iset->GetFieldInfo().GetFieldIndex(),
445 iset->GetFieldInfo().GetDexFile(),
446 /* with type */ false);
447 StartAttributeStream("field_type") << iset->GetFieldType();
448 }
449
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * field_access)450 void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
451 StartAttributeStream("field_type") << field_access->GetFieldType();
452 }
453
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * field_access)454 void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) OVERRIDE {
455 StartAttributeStream("field_type") << field_access->GetFieldType();
456 }
457
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * field_access)458 void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) OVERRIDE {
459 StartAttributeStream("field_type") << field_access->GetFieldType();
460 }
461
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * field_access)462 void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) OVERRIDE {
463 StartAttributeStream("field_type") << field_access->GetFieldType();
464 }
465
VisitTryBoundary(HTryBoundary * try_boundary)466 void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
467 StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
468 }
469
470 #if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
VisitMultiplyAccumulate(HMultiplyAccumulate * instruction)471 void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE {
472 StartAttributeStream("kind") << instruction->GetOpKind();
473 }
474
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)475 void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE {
476 StartAttributeStream("kind") << instruction->GetOpKind();
477 }
478 #endif
479
480 #ifdef ART_ENABLE_CODEGEN_arm64
VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp * instruction)481 void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE {
482 StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind();
483 if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
484 StartAttributeStream("shift") << instruction->GetShiftAmount();
485 }
486 }
487 #endif
488
IsPass(const char * name)489 bool IsPass(const char* name) {
490 return strcmp(pass_name_, name) == 0;
491 }
492
PrintInstruction(HInstruction * instruction)493 void PrintInstruction(HInstruction* instruction) {
494 output_ << instruction->DebugName();
495 if (instruction->InputCount() > 0) {
496 StringList inputs;
497 for (HInputIterator it(instruction); !it.Done(); it.Advance()) {
498 inputs.NewEntryStream() << GetTypeId(it.Current()->GetType()) << it.Current()->GetId();
499 }
500 StartAttributeStream() << inputs;
501 }
502 instruction->Accept(this);
503 if (instruction->HasEnvironment()) {
504 StringList envs;
505 for (HEnvironment* environment = instruction->GetEnvironment();
506 environment != nullptr;
507 environment = environment->GetParent()) {
508 StringList vregs;
509 for (size_t i = 0, e = environment->Size(); i < e; ++i) {
510 HInstruction* insn = environment->GetInstructionAt(i);
511 if (insn != nullptr) {
512 vregs.NewEntryStream() << GetTypeId(insn->GetType()) << insn->GetId();
513 } else {
514 vregs.NewEntryStream() << "_";
515 }
516 }
517 envs.NewEntryStream() << vregs;
518 }
519 StartAttributeStream("env") << envs;
520 }
521 if (IsPass(SsaLivenessAnalysis::kLivenessPassName)
522 && is_after_pass_
523 && instruction->GetLifetimePosition() != kNoLifetime) {
524 StartAttributeStream("liveness") << instruction->GetLifetimePosition();
525 if (instruction->HasLiveInterval()) {
526 LiveInterval* interval = instruction->GetLiveInterval();
527 StartAttributeStream("ranges")
528 << StringList(interval->GetFirstRange(), StringList::kSetBrackets);
529 StartAttributeStream("uses") << StringList(interval->GetFirstUse());
530 StartAttributeStream("env_uses") << StringList(interval->GetFirstEnvironmentUse());
531 StartAttributeStream("is_fixed") << interval->IsFixed();
532 StartAttributeStream("is_split") << interval->IsSplit();
533 StartAttributeStream("is_low") << interval->IsLowInterval();
534 StartAttributeStream("is_high") << interval->IsHighInterval();
535 }
536 }
537
538 if (IsPass(RegisterAllocator::kRegisterAllocatorPassName) && is_after_pass_) {
539 StartAttributeStream("liveness") << instruction->GetLifetimePosition();
540 LocationSummary* locations = instruction->GetLocations();
541 if (locations != nullptr) {
542 StringList inputs;
543 for (size_t i = 0; i < instruction->InputCount(); ++i) {
544 DumpLocation(inputs.NewEntryStream(), locations->InAt(i));
545 }
546 std::ostream& attr = StartAttributeStream("locations");
547 attr << inputs << "->";
548 DumpLocation(attr, locations->Out());
549 }
550 }
551
552 HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
553 if (loop_info == nullptr) {
554 StartAttributeStream("loop") << "none";
555 } else {
556 StartAttributeStream("loop") << "B" << loop_info->GetHeader()->GetBlockId();
557 HLoopInformation* outer = loop_info->GetPreHeader()->GetLoopInformation();
558 if (outer != nullptr) {
559 StartAttributeStream("outer_loop") << "B" << outer->GetHeader()->GetBlockId();
560 } else {
561 StartAttributeStream("outer_loop") << "none";
562 }
563 StartAttributeStream("irreducible")
564 << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha;
565 }
566
567 if ((IsPass(HGraphBuilder::kBuilderPassName)
568 || IsPass(HInliner::kInlinerPassName))
569 && (instruction->GetType() == Primitive::kPrimNot)) {
570 ReferenceTypeInfo info = instruction->IsLoadClass()
571 ? instruction->AsLoadClass()->GetLoadedClassRTI()
572 : instruction->GetReferenceTypeInfo();
573 ScopedObjectAccess soa(Thread::Current());
574 if (info.IsValid()) {
575 StartAttributeStream("klass") << PrettyDescriptor(info.GetTypeHandle().Get());
576 StartAttributeStream("can_be_null")
577 << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
578 StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
579 } else if (instruction->IsLoadClass()) {
580 StartAttributeStream("klass") << "unresolved";
581 } else {
582 // The NullConstant may be added to the graph during other passes that happen between
583 // ReferenceTypePropagation and Inliner (e.g. InstructionSimplifier). If the inliner
584 // doesn't run or doesn't inline anything, the NullConstant remains untyped.
585 // So we should check NullConstants for validity only after reference type propagation.
586 DCHECK(graph_in_bad_state_ ||
587 (!is_after_pass_ && IsPass(HGraphBuilder::kBuilderPassName)))
588 << instruction->DebugName() << instruction->GetId() << " has invalid rti "
589 << (is_after_pass_ ? "after" : "before") << " pass " << pass_name_;
590 }
591 }
592 if (disasm_info_ != nullptr) {
593 DCHECK(disassembler_ != nullptr);
594 // If the information is available, disassemble the code generated for
595 // this instruction.
596 auto it = disasm_info_->GetInstructionIntervals().find(instruction);
597 if (it != disasm_info_->GetInstructionIntervals().end()
598 && it->second.start != it->second.end) {
599 output_ << "\n";
600 disassembler_->Disassemble(output_, it->second.start, it->second.end);
601 }
602 }
603 }
604
PrintInstructions(const HInstructionList & list)605 void PrintInstructions(const HInstructionList& list) {
606 for (HInstructionIterator it(list); !it.Done(); it.Advance()) {
607 HInstruction* instruction = it.Current();
608 int bci = 0;
609 size_t num_uses = instruction->GetUses().SizeSlow();
610 AddIndent();
611 output_ << bci << " " << num_uses << " "
612 << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
613 PrintInstruction(instruction);
614 output_ << " " << kEndInstructionMarker << "\n";
615 }
616 }
617
DumpStartOfDisassemblyBlock(const char * block_name,int predecessor_index,int successor_index)618 void DumpStartOfDisassemblyBlock(const char* block_name,
619 int predecessor_index,
620 int successor_index) {
621 StartTag("block");
622 PrintProperty("name", block_name);
623 PrintInt("from_bci", -1);
624 PrintInt("to_bci", -1);
625 if (predecessor_index != -1) {
626 PrintProperty("predecessors", "B", predecessor_index);
627 } else {
628 PrintEmptyProperty("predecessors");
629 }
630 if (successor_index != -1) {
631 PrintProperty("successors", "B", successor_index);
632 } else {
633 PrintEmptyProperty("successors");
634 }
635 PrintEmptyProperty("xhandlers");
636 PrintEmptyProperty("flags");
637 StartTag("states");
638 StartTag("locals");
639 PrintInt("size", 0);
640 PrintProperty("method", "None");
641 EndTag("locals");
642 EndTag("states");
643 StartTag("HIR");
644 }
645
DumpEndOfDisassemblyBlock()646 void DumpEndOfDisassemblyBlock() {
647 EndTag("HIR");
648 EndTag("block");
649 }
650
DumpDisassemblyBlockForFrameEntry()651 void DumpDisassemblyBlockForFrameEntry() {
652 DumpStartOfDisassemblyBlock(kDisassemblyBlockFrameEntry,
653 -1,
654 GetGraph()->GetEntryBlock()->GetBlockId());
655 output_ << " 0 0 disasm " << kDisassemblyBlockFrameEntry << " ";
656 GeneratedCodeInterval frame_entry = disasm_info_->GetFrameEntryInterval();
657 if (frame_entry.start != frame_entry.end) {
658 output_ << "\n";
659 disassembler_->Disassemble(output_, frame_entry.start, frame_entry.end);
660 }
661 output_ << kEndInstructionMarker << "\n";
662 DumpEndOfDisassemblyBlock();
663 }
664
DumpDisassemblyBlockForSlowPaths()665 void DumpDisassemblyBlockForSlowPaths() {
666 if (disasm_info_->GetSlowPathIntervals().empty()) {
667 return;
668 }
669 // If the graph has an exit block we attach the block for the slow paths
670 // after it. Else we just add the block to the graph without linking it to
671 // any other.
672 DumpStartOfDisassemblyBlock(
673 kDisassemblyBlockSlowPaths,
674 GetGraph()->HasExitBlock() ? GetGraph()->GetExitBlock()->GetBlockId() : -1,
675 -1);
676 for (SlowPathCodeInfo info : disasm_info_->GetSlowPathIntervals()) {
677 output_ << " 0 0 disasm " << info.slow_path->GetDescription() << "\n";
678 disassembler_->Disassemble(output_, info.code_interval.start, info.code_interval.end);
679 output_ << kEndInstructionMarker << "\n";
680 }
681 DumpEndOfDisassemblyBlock();
682 }
683
Run()684 void Run() {
685 StartTag("cfg");
686 std::string pass_desc = std::string(pass_name_)
687 + " ("
688 + (is_after_pass_ ? "after" : "before")
689 + (graph_in_bad_state_ ? ", bad_state" : "")
690 + ")";
691 PrintProperty("name", pass_desc.c_str());
692 if (disasm_info_ != nullptr) {
693 DumpDisassemblyBlockForFrameEntry();
694 }
695 VisitInsertionOrder();
696 if (disasm_info_ != nullptr) {
697 DumpDisassemblyBlockForSlowPaths();
698 }
699 EndTag("cfg");
700 Flush();
701 }
702
VisitBasicBlock(HBasicBlock * block)703 void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
704 StartTag("block");
705 PrintProperty("name", "B", block->GetBlockId());
706 if (block->GetLifetimeStart() != kNoLifetime) {
707 // Piggy back on these fields to show the lifetime of the block.
708 PrintInt("from_bci", block->GetLifetimeStart());
709 PrintInt("to_bci", block->GetLifetimeEnd());
710 } else {
711 PrintInt("from_bci", -1);
712 PrintInt("to_bci", -1);
713 }
714 PrintPredecessors(block);
715 PrintSuccessors(block);
716 PrintExceptionHandlers(block);
717
718 if (block->IsCatchBlock()) {
719 PrintProperty("flags", "catch_block");
720 } else {
721 PrintEmptyProperty("flags");
722 }
723
724 if (block->GetDominator() != nullptr) {
725 PrintProperty("dominator", "B", block->GetDominator()->GetBlockId());
726 }
727
728 StartTag("states");
729 StartTag("locals");
730 PrintInt("size", 0);
731 PrintProperty("method", "None");
732 for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
733 AddIndent();
734 HInstruction* instruction = it.Current();
735 output_ << instruction->GetId() << " " << GetTypeId(instruction->GetType())
736 << instruction->GetId() << "[ ";
737 for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
738 output_ << inputs.Current()->GetId() << " ";
739 }
740 output_ << "]\n";
741 }
742 EndTag("locals");
743 EndTag("states");
744
745 StartTag("HIR");
746 PrintInstructions(block->GetPhis());
747 PrintInstructions(block->GetInstructions());
748 EndTag("HIR");
749 EndTag("block");
750 }
751
752 static constexpr const char* const kEndInstructionMarker = "<|@";
753 static constexpr const char* const kDisassemblyBlockFrameEntry = "FrameEntry";
754 static constexpr const char* const kDisassemblyBlockSlowPaths = "SlowPaths";
755
756 private:
757 std::ostream& output_;
758 const char* pass_name_;
759 const bool is_after_pass_;
760 const bool graph_in_bad_state_;
761 const CodeGenerator& codegen_;
762 const DisassemblyInformation* disasm_info_;
763 std::unique_ptr<HGraphVisualizerDisassembler> disassembler_;
764 size_t indent_;
765
766 DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter);
767 };
768
HGraphVisualizer(std::ostream * output,HGraph * graph,const CodeGenerator & codegen)769 HGraphVisualizer::HGraphVisualizer(std::ostream* output,
770 HGraph* graph,
771 const CodeGenerator& codegen)
772 : output_(output), graph_(graph), codegen_(codegen) {}
773
PrintHeader(const char * method_name) const774 void HGraphVisualizer::PrintHeader(const char* method_name) const {
775 DCHECK(output_ != nullptr);
776 HGraphVisualizerPrinter printer(graph_, *output_, "", true, false, codegen_);
777 printer.StartTag("compilation");
778 printer.PrintProperty("name", method_name);
779 printer.PrintProperty("method", method_name);
780 printer.PrintTime("date");
781 printer.EndTag("compilation");
782 printer.Flush();
783 }
784
DumpGraph(const char * pass_name,bool is_after_pass,bool graph_in_bad_state) const785 void HGraphVisualizer::DumpGraph(const char* pass_name,
786 bool is_after_pass,
787 bool graph_in_bad_state) const {
788 DCHECK(output_ != nullptr);
789 if (!graph_->GetBlocks().empty()) {
790 HGraphVisualizerPrinter printer(graph_,
791 *output_,
792 pass_name,
793 is_after_pass,
794 graph_in_bad_state,
795 codegen_);
796 printer.Run();
797 }
798 }
799
DumpGraphWithDisassembly() const800 void HGraphVisualizer::DumpGraphWithDisassembly() const {
801 DCHECK(output_ != nullptr);
802 if (!graph_->GetBlocks().empty()) {
803 HGraphVisualizerPrinter printer(graph_,
804 *output_,
805 "disassembly",
806 /* is_after_pass */ true,
807 /* graph_in_bad_state */ false,
808 codegen_,
809 codegen_.GetDisassemblyInformation());
810 printer.Run();
811 }
812 }
813
814 } // namespace art
815