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_linear_scan.h" 35#include "ssa_liveness_analysis.h" 36#include "utils/assembler.h" 37 38namespace art { 39 40static 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 51class StringList { 52 public: 53 enum Format { 54 kArrayBrackets, 55 kSetBrackets, 56 }; 57 58 // Create an empty list 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> 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 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 87std::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 97typedef Disassembler* create_disasm_prototype(InstructionSet instruction_set, 98 DisassemblerOptions* options); 99class HGraphVisualizerDisassembler { 100 public: 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 Is64BitInstructionSet(instruction_set) 127 ? &Thread::DumpThreadOffset<PointerSize::k64> 128 : &Thread::DumpThreadOffset<PointerSize::k32>))); 129 } 130 131 ~HGraphVisualizerDisassembler() { 132 // We need to call ~Disassembler() before we close the library. 133 disassembler_.reset(); 134 if (libart_disassembler_handle_ != nullptr) { 135 dlclose(libart_disassembler_handle_); 136 } 137 } 138 139 void Disassemble(std::ostream& output, size_t start, size_t end) const { 140 if (disassembler_ == nullptr) { 141 return; 142 } 143 144 const uint8_t* base = disassembler_->GetDisassemblerOptions()->base_address_; 145 if (instruction_set_ == kThumb2) { 146 // ARM and Thumb-2 use the same disassembler. The bottom bit of the 147 // address is used to distinguish between the two. 148 base += 1; 149 } 150 disassembler_->Dump(output, base + start, base + end); 151 } 152 153 private: 154 InstructionSet instruction_set_; 155 std::unique_ptr<Disassembler> disassembler_; 156 157 void* libart_disassembler_handle_; 158}; 159 160 161/** 162 * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra. 163 */ 164class HGraphVisualizerPrinter : public HGraphDelegateVisitor { 165 public: 166 HGraphVisualizerPrinter(HGraph* graph, 167 std::ostream& output, 168 const char* pass_name, 169 bool is_after_pass, 170 bool graph_in_bad_state, 171 const CodeGenerator& codegen, 172 const DisassemblyInformation* disasm_info = nullptr) 173 : HGraphDelegateVisitor(graph), 174 output_(output), 175 pass_name_(pass_name), 176 is_after_pass_(is_after_pass), 177 graph_in_bad_state_(graph_in_bad_state), 178 codegen_(codegen), 179 disasm_info_(disasm_info), 180 disassembler_(disasm_info_ != nullptr 181 ? new HGraphVisualizerDisassembler( 182 codegen_.GetInstructionSet(), 183 codegen_.GetAssembler().CodeBufferBaseAddress(), 184 codegen_.GetAssembler().CodeBufferBaseAddress() 185 + codegen_.GetAssembler().CodeSize()) 186 : nullptr), 187 indent_(0) {} 188 189 void Flush() { 190 // We use "\n" instead of std::endl to avoid implicit flushing which 191 // generates too many syscalls during debug-GC tests (b/27826765). 192 output_ << std::flush; 193 } 194 195 void StartTag(const char* name) { 196 AddIndent(); 197 output_ << "begin_" << name << "\n"; 198 indent_++; 199 } 200 201 void EndTag(const char* name) { 202 indent_--; 203 AddIndent(); 204 output_ << "end_" << name << "\n"; 205 } 206 207 void PrintProperty(const char* name, const char* property) { 208 AddIndent(); 209 output_ << name << " \"" << property << "\"\n"; 210 } 211 212 void PrintProperty(const char* name, const char* property, int id) { 213 AddIndent(); 214 output_ << name << " \"" << property << id << "\"\n"; 215 } 216 217 void PrintEmptyProperty(const char* name) { 218 AddIndent(); 219 output_ << name << "\n"; 220 } 221 222 void PrintTime(const char* name) { 223 AddIndent(); 224 output_ << name << " " << time(nullptr) << "\n"; 225 } 226 227 void PrintInt(const char* name, int value) { 228 AddIndent(); 229 output_ << name << " " << value << "\n"; 230 } 231 232 void AddIndent() { 233 for (size_t i = 0; i < indent_; ++i) { 234 output_ << " "; 235 } 236 } 237 238 char GetTypeId(Primitive::Type type) { 239 // Note that Primitive::Descriptor would not work for us 240 // because it does not handle reference types (that is kPrimNot). 241 switch (type) { 242 case Primitive::kPrimBoolean: return 'z'; 243 case Primitive::kPrimByte: return 'b'; 244 case Primitive::kPrimChar: return 'c'; 245 case Primitive::kPrimShort: return 's'; 246 case Primitive::kPrimInt: return 'i'; 247 case Primitive::kPrimLong: return 'j'; 248 case Primitive::kPrimFloat: return 'f'; 249 case Primitive::kPrimDouble: return 'd'; 250 case Primitive::kPrimNot: return 'l'; 251 case Primitive::kPrimVoid: return 'v'; 252 } 253 LOG(FATAL) << "Unreachable"; 254 return 'v'; 255 } 256 257 void PrintPredecessors(HBasicBlock* block) { 258 AddIndent(); 259 output_ << "predecessors"; 260 for (HBasicBlock* predecessor : block->GetPredecessors()) { 261 output_ << " \"B" << predecessor->GetBlockId() << "\" "; 262 } 263 if (block->IsEntryBlock() && (disasm_info_ != nullptr)) { 264 output_ << " \"" << kDisassemblyBlockFrameEntry << "\" "; 265 } 266 output_<< "\n"; 267 } 268 269 void PrintSuccessors(HBasicBlock* block) { 270 AddIndent(); 271 output_ << "successors"; 272 for (HBasicBlock* successor : block->GetNormalSuccessors()) { 273 output_ << " \"B" << successor->GetBlockId() << "\" "; 274 } 275 output_<< "\n"; 276 } 277 278 void PrintExceptionHandlers(HBasicBlock* block) { 279 AddIndent(); 280 output_ << "xhandlers"; 281 for (HBasicBlock* handler : block->GetExceptionalSuccessors()) { 282 output_ << " \"B" << handler->GetBlockId() << "\" "; 283 } 284 if (block->IsExitBlock() && 285 (disasm_info_ != nullptr) && 286 !disasm_info_->GetSlowPathIntervals().empty()) { 287 output_ << " \"" << kDisassemblyBlockSlowPaths << "\" "; 288 } 289 output_<< "\n"; 290 } 291 292 void DumpLocation(std::ostream& stream, const Location& location) { 293 if (location.IsRegister()) { 294 codegen_.DumpCoreRegister(stream, location.reg()); 295 } else if (location.IsFpuRegister()) { 296 codegen_.DumpFloatingPointRegister(stream, location.reg()); 297 } else if (location.IsConstant()) { 298 stream << "#"; 299 HConstant* constant = location.GetConstant(); 300 if (constant->IsIntConstant()) { 301 stream << constant->AsIntConstant()->GetValue(); 302 } else if (constant->IsLongConstant()) { 303 stream << constant->AsLongConstant()->GetValue(); 304 } else if (constant->IsFloatConstant()) { 305 stream << constant->AsFloatConstant()->GetValue(); 306 } else if (constant->IsDoubleConstant()) { 307 stream << constant->AsDoubleConstant()->GetValue(); 308 } else if (constant->IsNullConstant()) { 309 stream << "null"; 310 } 311 } else if (location.IsInvalid()) { 312 stream << "invalid"; 313 } else if (location.IsStackSlot()) { 314 stream << location.GetStackIndex() << "(sp)"; 315 } else if (location.IsFpuRegisterPair()) { 316 codegen_.DumpFloatingPointRegister(stream, location.low()); 317 stream << "|"; 318 codegen_.DumpFloatingPointRegister(stream, location.high()); 319 } else if (location.IsRegisterPair()) { 320 codegen_.DumpCoreRegister(stream, location.low()); 321 stream << "|"; 322 codegen_.DumpCoreRegister(stream, location.high()); 323 } else if (location.IsUnallocated()) { 324 stream << "unallocated"; 325 } else if (location.IsDoubleStackSlot()) { 326 stream << "2x" << location.GetStackIndex() << "(sp)"; 327 } else { 328 DCHECK(location.IsSIMDStackSlot()); 329 stream << "4x" << location.GetStackIndex() << "(sp)"; 330 } 331 } 332 333 std::ostream& StartAttributeStream(const char* name = nullptr) { 334 if (name == nullptr) { 335 output_ << " "; 336 } else { 337 DCHECK(!HasWhitespace(name)) << "Checker does not allow spaces in attributes"; 338 output_ << " " << name << ":"; 339 } 340 return output_; 341 } 342 343 void VisitParallelMove(HParallelMove* instruction) OVERRIDE { 344 StartAttributeStream("liveness") << instruction->GetLifetimePosition(); 345 StringList moves; 346 for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) { 347 MoveOperands* move = instruction->MoveOperandsAt(i); 348 std::ostream& str = moves.NewEntryStream(); 349 DumpLocation(str, move->GetSource()); 350 str << "->"; 351 DumpLocation(str, move->GetDestination()); 352 } 353 StartAttributeStream("moves") << moves; 354 } 355 356 void VisitIntConstant(HIntConstant* instruction) OVERRIDE { 357 StartAttributeStream() << instruction->GetValue(); 358 } 359 360 void VisitLongConstant(HLongConstant* instruction) OVERRIDE { 361 StartAttributeStream() << instruction->GetValue(); 362 } 363 364 void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE { 365 StartAttributeStream() << instruction->GetValue(); 366 } 367 368 void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE { 369 StartAttributeStream() << instruction->GetValue(); 370 } 371 372 void VisitPhi(HPhi* phi) OVERRIDE { 373 StartAttributeStream("reg") << phi->GetRegNumber(); 374 StartAttributeStream("is_catch_phi") << std::boolalpha << phi->IsCatchPhi() << std::noboolalpha; 375 } 376 377 void VisitMemoryBarrier(HMemoryBarrier* barrier) OVERRIDE { 378 StartAttributeStream("kind") << barrier->GetBarrierKind(); 379 } 380 381 void VisitMonitorOperation(HMonitorOperation* monitor) OVERRIDE { 382 StartAttributeStream("kind") << (monitor->IsEnter() ? "enter" : "exit"); 383 } 384 385 void VisitLoadClass(HLoadClass* load_class) OVERRIDE { 386 StartAttributeStream("load_kind") << load_class->GetLoadKind(); 387 const char* descriptor = load_class->GetDexFile().GetTypeDescriptor( 388 load_class->GetDexFile().GetTypeId(load_class->GetTypeIndex())); 389 StartAttributeStream("class_name") << PrettyDescriptor(descriptor); 390 StartAttributeStream("gen_clinit_check") << std::boolalpha 391 << load_class->MustGenerateClinitCheck() << std::noboolalpha; 392 StartAttributeStream("needs_access_check") << std::boolalpha 393 << load_class->NeedsAccessCheck() << std::noboolalpha; 394 } 395 396 void VisitLoadString(HLoadString* load_string) OVERRIDE { 397 StartAttributeStream("load_kind") << load_string->GetLoadKind(); 398 } 399 400 void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { 401 StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind(); 402 StartAttributeStream("must_do_null_check") << std::boolalpha 403 << check_cast->MustDoNullCheck() << std::noboolalpha; 404 } 405 406 void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE { 407 StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind(); 408 StartAttributeStream("must_do_null_check") << std::boolalpha 409 << instance_of->MustDoNullCheck() << std::noboolalpha; 410 } 411 412 void VisitArrayLength(HArrayLength* array_length) OVERRIDE { 413 StartAttributeStream("is_string_length") << std::boolalpha 414 << array_length->IsStringLength() << std::noboolalpha; 415 if (array_length->IsEmittedAtUseSite()) { 416 StartAttributeStream("emitted_at_use") << "true"; 417 } 418 } 419 420 void VisitBoundsCheck(HBoundsCheck* bounds_check) OVERRIDE { 421 StartAttributeStream("is_string_char_at") << std::boolalpha 422 << bounds_check->IsStringCharAt() << std::noboolalpha; 423 } 424 425 void VisitArrayGet(HArrayGet* array_get) OVERRIDE { 426 StartAttributeStream("is_string_char_at") << std::boolalpha 427 << array_get->IsStringCharAt() << std::noboolalpha; 428 } 429 430 void VisitArraySet(HArraySet* array_set) OVERRIDE { 431 StartAttributeStream("value_can_be_null") << std::boolalpha 432 << array_set->GetValueCanBeNull() << std::noboolalpha; 433 StartAttributeStream("needs_type_check") << std::boolalpha 434 << array_set->NeedsTypeCheck() << std::noboolalpha; 435 } 436 437 void VisitCompare(HCompare* compare) OVERRIDE { 438 ComparisonBias bias = compare->GetBias(); 439 StartAttributeStream("bias") << (bias == ComparisonBias::kGtBias 440 ? "gt" 441 : (bias == ComparisonBias::kLtBias ? "lt" : "none")); 442 } 443 444 void VisitInvoke(HInvoke* invoke) OVERRIDE { 445 StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex(); 446 StartAttributeStream("method_name") << GetGraph()->GetDexFile().PrettyMethod( 447 invoke->GetDexMethodIndex(), /* with_signature */ false); 448 } 449 450 void VisitInvokeUnresolved(HInvokeUnresolved* invoke) OVERRIDE { 451 VisitInvoke(invoke); 452 StartAttributeStream("invoke_type") << invoke->GetInvokeType(); 453 } 454 455 void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { 456 VisitInvoke(invoke); 457 StartAttributeStream("method_load_kind") << invoke->GetMethodLoadKind(); 458 StartAttributeStream("intrinsic") << invoke->GetIntrinsic(); 459 if (invoke->IsStatic()) { 460 StartAttributeStream("clinit_check") << invoke->GetClinitCheckRequirement(); 461 } 462 } 463 464 void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE { 465 VisitInvoke(invoke); 466 StartAttributeStream("intrinsic") << invoke->GetIntrinsic(); 467 } 468 469 void VisitInvokePolymorphic(HInvokePolymorphic* invoke) OVERRIDE { 470 VisitInvoke(invoke); 471 StartAttributeStream("invoke_type") << "InvokePolymorphic"; 472 } 473 474 void VisitInstanceFieldGet(HInstanceFieldGet* iget) OVERRIDE { 475 StartAttributeStream("field_name") << 476 iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(), 477 /* with type */ false); 478 StartAttributeStream("field_type") << iget->GetFieldType(); 479 } 480 481 void VisitInstanceFieldSet(HInstanceFieldSet* iset) OVERRIDE { 482 StartAttributeStream("field_name") << 483 iset->GetFieldInfo().GetDexFile().PrettyField(iset->GetFieldInfo().GetFieldIndex(), 484 /* with type */ false); 485 StartAttributeStream("field_type") << iset->GetFieldType(); 486 } 487 488 void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE { 489 StartAttributeStream("field_type") << field_access->GetFieldType(); 490 } 491 492 void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) OVERRIDE { 493 StartAttributeStream("field_type") << field_access->GetFieldType(); 494 } 495 496 void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) OVERRIDE { 497 StartAttributeStream("field_type") << field_access->GetFieldType(); 498 } 499 500 void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) OVERRIDE { 501 StartAttributeStream("field_type") << field_access->GetFieldType(); 502 } 503 504 void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE { 505 StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); 506 } 507 508 void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE { 509 StartAttributeStream("kind") << deoptimize->GetKind(); 510 } 511 512 void VisitVecHalvingAdd(HVecHalvingAdd* hadd) OVERRIDE { 513 StartAttributeStream("unsigned") << std::boolalpha << hadd->IsUnsigned() << std::noboolalpha; 514 StartAttributeStream("rounded") << std::boolalpha << hadd->IsRounded() << std::noboolalpha; 515 } 516 517 void VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) OVERRIDE { 518 StartAttributeStream("kind") << instruction->GetOpKind(); 519 } 520 521#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) 522 void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { 523 StartAttributeStream("kind") << instruction->GetOpKind(); 524 } 525 526 void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE { 527 StartAttributeStream("kind") << instruction->GetOpKind(); 528 } 529 530 void VisitDataProcWithShifterOp(HDataProcWithShifterOp* instruction) OVERRIDE { 531 StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); 532 if (HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { 533 StartAttributeStream("shift") << instruction->GetShiftAmount(); 534 } 535 } 536#endif 537 538 bool IsPass(const char* name) { 539 return strcmp(pass_name_, name) == 0; 540 } 541 542 void PrintInstruction(HInstruction* instruction) { 543 output_ << instruction->DebugName(); 544 HConstInputsRef inputs = instruction->GetInputs(); 545 if (!inputs.empty()) { 546 StringList input_list; 547 for (const HInstruction* input : inputs) { 548 input_list.NewEntryStream() << GetTypeId(input->GetType()) << input->GetId(); 549 } 550 StartAttributeStream() << input_list; 551 } 552 instruction->Accept(this); 553 if (instruction->HasEnvironment()) { 554 StringList envs; 555 for (HEnvironment* environment = instruction->GetEnvironment(); 556 environment != nullptr; 557 environment = environment->GetParent()) { 558 StringList vregs; 559 for (size_t i = 0, e = environment->Size(); i < e; ++i) { 560 HInstruction* insn = environment->GetInstructionAt(i); 561 if (insn != nullptr) { 562 vregs.NewEntryStream() << GetTypeId(insn->GetType()) << insn->GetId(); 563 } else { 564 vregs.NewEntryStream() << "_"; 565 } 566 } 567 envs.NewEntryStream() << vregs; 568 } 569 StartAttributeStream("env") << envs; 570 } 571 if (IsPass(SsaLivenessAnalysis::kLivenessPassName) 572 && is_after_pass_ 573 && instruction->GetLifetimePosition() != kNoLifetime) { 574 StartAttributeStream("liveness") << instruction->GetLifetimePosition(); 575 if (instruction->HasLiveInterval()) { 576 LiveInterval* interval = instruction->GetLiveInterval(); 577 StartAttributeStream("ranges") 578 << StringList(interval->GetFirstRange(), StringList::kSetBrackets); 579 StartAttributeStream("uses") << StringList(interval->GetFirstUse()); 580 StartAttributeStream("env_uses") << StringList(interval->GetFirstEnvironmentUse()); 581 StartAttributeStream("is_fixed") << interval->IsFixed(); 582 StartAttributeStream("is_split") << interval->IsSplit(); 583 StartAttributeStream("is_low") << interval->IsLowInterval(); 584 StartAttributeStream("is_high") << interval->IsHighInterval(); 585 } 586 } 587 588 if (IsPass(RegisterAllocator::kRegisterAllocatorPassName) && is_after_pass_) { 589 StartAttributeStream("liveness") << instruction->GetLifetimePosition(); 590 LocationSummary* locations = instruction->GetLocations(); 591 if (locations != nullptr) { 592 StringList input_list; 593 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) { 594 DumpLocation(input_list.NewEntryStream(), locations->InAt(i)); 595 } 596 std::ostream& attr = StartAttributeStream("locations"); 597 attr << input_list << "->"; 598 DumpLocation(attr, locations->Out()); 599 } 600 } 601 602 HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation(); 603 if (loop_info == nullptr) { 604 StartAttributeStream("loop") << "none"; 605 } else { 606 StartAttributeStream("loop") << "B" << loop_info->GetHeader()->GetBlockId(); 607 HLoopInformation* outer = loop_info->GetPreHeader()->GetLoopInformation(); 608 if (outer != nullptr) { 609 StartAttributeStream("outer_loop") << "B" << outer->GetHeader()->GetBlockId(); 610 } else { 611 StartAttributeStream("outer_loop") << "none"; 612 } 613 StartAttributeStream("irreducible") 614 << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha; 615 } 616 617 if ((IsPass(HGraphBuilder::kBuilderPassName) 618 || IsPass(HInliner::kInlinerPassName)) 619 && (instruction->GetType() == Primitive::kPrimNot)) { 620 ReferenceTypeInfo info = instruction->IsLoadClass() 621 ? instruction->AsLoadClass()->GetLoadedClassRTI() 622 : instruction->GetReferenceTypeInfo(); 623 ScopedObjectAccess soa(Thread::Current()); 624 if (info.IsValid()) { 625 StartAttributeStream("klass") 626 << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get()); 627 StartAttributeStream("can_be_null") 628 << std::boolalpha << instruction->CanBeNull() << std::noboolalpha; 629 StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha; 630 } else if (instruction->IsLoadClass()) { 631 StartAttributeStream("klass") << "unresolved"; 632 } else { 633 // The NullConstant may be added to the graph during other passes that happen between 634 // ReferenceTypePropagation and Inliner (e.g. InstructionSimplifier). If the inliner 635 // doesn't run or doesn't inline anything, the NullConstant remains untyped. 636 // So we should check NullConstants for validity only after reference type propagation. 637 DCHECK(graph_in_bad_state_ || 638 (!is_after_pass_ && IsPass(HGraphBuilder::kBuilderPassName))) 639 << instruction->DebugName() << instruction->GetId() << " has invalid rti " 640 << (is_after_pass_ ? "after" : "before") << " pass " << pass_name_; 641 } 642 } 643 if (disasm_info_ != nullptr) { 644 DCHECK(disassembler_ != nullptr); 645 // If the information is available, disassemble the code generated for 646 // this instruction. 647 auto it = disasm_info_->GetInstructionIntervals().find(instruction); 648 if (it != disasm_info_->GetInstructionIntervals().end() 649 && it->second.start != it->second.end) { 650 output_ << "\n"; 651 disassembler_->Disassemble(output_, it->second.start, it->second.end); 652 } 653 } 654 } 655 656 void PrintInstructions(const HInstructionList& list) { 657 for (HInstructionIterator it(list); !it.Done(); it.Advance()) { 658 HInstruction* instruction = it.Current(); 659 int bci = 0; 660 size_t num_uses = instruction->GetUses().SizeSlow(); 661 AddIndent(); 662 output_ << bci << " " << num_uses << " " 663 << GetTypeId(instruction->GetType()) << instruction->GetId() << " "; 664 PrintInstruction(instruction); 665 output_ << " " << kEndInstructionMarker << "\n"; 666 } 667 } 668 669 void DumpStartOfDisassemblyBlock(const char* block_name, 670 int predecessor_index, 671 int successor_index) { 672 StartTag("block"); 673 PrintProperty("name", block_name); 674 PrintInt("from_bci", -1); 675 PrintInt("to_bci", -1); 676 if (predecessor_index != -1) { 677 PrintProperty("predecessors", "B", predecessor_index); 678 } else { 679 PrintEmptyProperty("predecessors"); 680 } 681 if (successor_index != -1) { 682 PrintProperty("successors", "B", successor_index); 683 } else { 684 PrintEmptyProperty("successors"); 685 } 686 PrintEmptyProperty("xhandlers"); 687 PrintEmptyProperty("flags"); 688 StartTag("states"); 689 StartTag("locals"); 690 PrintInt("size", 0); 691 PrintProperty("method", "None"); 692 EndTag("locals"); 693 EndTag("states"); 694 StartTag("HIR"); 695 } 696 697 void DumpEndOfDisassemblyBlock() { 698 EndTag("HIR"); 699 EndTag("block"); 700 } 701 702 void DumpDisassemblyBlockForFrameEntry() { 703 DumpStartOfDisassemblyBlock(kDisassemblyBlockFrameEntry, 704 -1, 705 GetGraph()->GetEntryBlock()->GetBlockId()); 706 output_ << " 0 0 disasm " << kDisassemblyBlockFrameEntry << " "; 707 GeneratedCodeInterval frame_entry = disasm_info_->GetFrameEntryInterval(); 708 if (frame_entry.start != frame_entry.end) { 709 output_ << "\n"; 710 disassembler_->Disassemble(output_, frame_entry.start, frame_entry.end); 711 } 712 output_ << kEndInstructionMarker << "\n"; 713 DumpEndOfDisassemblyBlock(); 714 } 715 716 void DumpDisassemblyBlockForSlowPaths() { 717 if (disasm_info_->GetSlowPathIntervals().empty()) { 718 return; 719 } 720 // If the graph has an exit block we attach the block for the slow paths 721 // after it. Else we just add the block to the graph without linking it to 722 // any other. 723 DumpStartOfDisassemblyBlock( 724 kDisassemblyBlockSlowPaths, 725 GetGraph()->HasExitBlock() ? GetGraph()->GetExitBlock()->GetBlockId() : -1, 726 -1); 727 for (SlowPathCodeInfo info : disasm_info_->GetSlowPathIntervals()) { 728 output_ << " 0 0 disasm " << info.slow_path->GetDescription() << "\n"; 729 disassembler_->Disassemble(output_, info.code_interval.start, info.code_interval.end); 730 output_ << kEndInstructionMarker << "\n"; 731 } 732 DumpEndOfDisassemblyBlock(); 733 } 734 735 void Run() { 736 StartTag("cfg"); 737 std::string pass_desc = std::string(pass_name_) 738 + " (" 739 + (is_after_pass_ ? "after" : "before") 740 + (graph_in_bad_state_ ? ", bad_state" : "") 741 + ")"; 742 PrintProperty("name", pass_desc.c_str()); 743 if (disasm_info_ != nullptr) { 744 DumpDisassemblyBlockForFrameEntry(); 745 } 746 VisitInsertionOrder(); 747 if (disasm_info_ != nullptr) { 748 DumpDisassemblyBlockForSlowPaths(); 749 } 750 EndTag("cfg"); 751 Flush(); 752 } 753 754 void VisitBasicBlock(HBasicBlock* block) OVERRIDE { 755 StartTag("block"); 756 PrintProperty("name", "B", block->GetBlockId()); 757 if (block->GetLifetimeStart() != kNoLifetime) { 758 // Piggy back on these fields to show the lifetime of the block. 759 PrintInt("from_bci", block->GetLifetimeStart()); 760 PrintInt("to_bci", block->GetLifetimeEnd()); 761 } else { 762 PrintInt("from_bci", -1); 763 PrintInt("to_bci", -1); 764 } 765 PrintPredecessors(block); 766 PrintSuccessors(block); 767 PrintExceptionHandlers(block); 768 769 if (block->IsCatchBlock()) { 770 PrintProperty("flags", "catch_block"); 771 } else { 772 PrintEmptyProperty("flags"); 773 } 774 775 if (block->GetDominator() != nullptr) { 776 PrintProperty("dominator", "B", block->GetDominator()->GetBlockId()); 777 } 778 779 StartTag("states"); 780 StartTag("locals"); 781 PrintInt("size", 0); 782 PrintProperty("method", "None"); 783 for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { 784 AddIndent(); 785 HInstruction* instruction = it.Current(); 786 output_ << instruction->GetId() << " " << GetTypeId(instruction->GetType()) 787 << instruction->GetId() << "[ "; 788 for (const HInstruction* input : instruction->GetInputs()) { 789 output_ << input->GetId() << " "; 790 } 791 output_ << "]\n"; 792 } 793 EndTag("locals"); 794 EndTag("states"); 795 796 StartTag("HIR"); 797 PrintInstructions(block->GetPhis()); 798 PrintInstructions(block->GetInstructions()); 799 EndTag("HIR"); 800 EndTag("block"); 801 } 802 803 static constexpr const char* const kEndInstructionMarker = "<|@"; 804 static constexpr const char* const kDisassemblyBlockFrameEntry = "FrameEntry"; 805 static constexpr const char* const kDisassemblyBlockSlowPaths = "SlowPaths"; 806 807 private: 808 std::ostream& output_; 809 const char* pass_name_; 810 const bool is_after_pass_; 811 const bool graph_in_bad_state_; 812 const CodeGenerator& codegen_; 813 const DisassemblyInformation* disasm_info_; 814 std::unique_ptr<HGraphVisualizerDisassembler> disassembler_; 815 size_t indent_; 816 817 DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter); 818}; 819 820HGraphVisualizer::HGraphVisualizer(std::ostream* output, 821 HGraph* graph, 822 const CodeGenerator& codegen) 823 : output_(output), graph_(graph), codegen_(codegen) {} 824 825void HGraphVisualizer::PrintHeader(const char* method_name) const { 826 DCHECK(output_ != nullptr); 827 HGraphVisualizerPrinter printer(graph_, *output_, "", true, false, codegen_); 828 printer.StartTag("compilation"); 829 printer.PrintProperty("name", method_name); 830 printer.PrintProperty("method", method_name); 831 printer.PrintTime("date"); 832 printer.EndTag("compilation"); 833 printer.Flush(); 834} 835 836void HGraphVisualizer::DumpGraph(const char* pass_name, 837 bool is_after_pass, 838 bool graph_in_bad_state) const { 839 DCHECK(output_ != nullptr); 840 if (!graph_->GetBlocks().empty()) { 841 HGraphVisualizerPrinter printer(graph_, 842 *output_, 843 pass_name, 844 is_after_pass, 845 graph_in_bad_state, 846 codegen_); 847 printer.Run(); 848 } 849} 850 851void HGraphVisualizer::DumpGraphWithDisassembly() const { 852 DCHECK(output_ != nullptr); 853 if (!graph_->GetBlocks().empty()) { 854 HGraphVisualizerPrinter printer(graph_, 855 *output_, 856 "disassembly", 857 /* is_after_pass */ true, 858 /* graph_in_bad_state */ false, 859 codegen_, 860 codegen_.GetDisassemblyInformation()); 861 printer.Run(); 862 } 863} 864 865} // namespace art 866