Home | History | Annotate | Download | only in optimizing
      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 
     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
     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 
     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:
    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 
    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 
    136   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:
    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 
    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 
    192   void StartTag(const char* name) {
    193     AddIndent();
    194     output_ << "begin_" << name << "\n";
    195     indent_++;
    196   }
    197 
    198   void EndTag(const char* name) {
    199     indent_--;
    200     AddIndent();
    201     output_ << "end_" << name << "\n";
    202   }
    203 
    204   void PrintProperty(const char* name, const char* property) {
    205     AddIndent();
    206     output_ << name << " \"" << property << "\"\n";
    207   }
    208 
    209   void PrintProperty(const char* name, const char* property, int id) {
    210     AddIndent();
    211     output_ << name << " \"" << property << id << "\"\n";
    212   }
    213 
    214   void PrintEmptyProperty(const char* name) {
    215     AddIndent();
    216     output_ << name << "\n";
    217   }
    218 
    219   void PrintTime(const char* name) {
    220     AddIndent();
    221     output_ << name << " " << time(nullptr) << "\n";
    222   }
    223 
    224   void PrintInt(const char* name, int value) {
    225     AddIndent();
    226     output_ << name << " " << value << "\n";
    227   }
    228 
    229   void AddIndent() {
    230     for (size_t i = 0; i < indent_; ++i) {
    231       output_ << "  ";
    232     }
    233   }
    234 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    345   void VisitIntConstant(HIntConstant* instruction) OVERRIDE {
    346     StartAttributeStream() << instruction->GetValue();
    347   }
    348 
    349   void VisitLongConstant(HLongConstant* instruction) OVERRIDE {
    350     StartAttributeStream() << instruction->GetValue();
    351   }
    352 
    353   void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE {
    354     StartAttributeStream() << instruction->GetValue();
    355   }
    356 
    357   void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE {
    358     StartAttributeStream() << instruction->GetValue();
    359   }
    360 
    361   void VisitPhi(HPhi* phi) OVERRIDE {
    362     StartAttributeStream("reg") << phi->GetRegNumber();
    363     StartAttributeStream("is_catch_phi") << std::boolalpha << phi->IsCatchPhi() << std::noboolalpha;
    364   }
    365 
    366   void VisitMemoryBarrier(HMemoryBarrier* barrier) OVERRIDE {
    367     StartAttributeStream("kind") << barrier->GetBarrierKind();
    368   }
    369 
    370   void VisitMonitorOperation(HMonitorOperation* monitor) OVERRIDE {
    371     StartAttributeStream("kind") << (monitor->IsEnter() ? "enter" : "exit");
    372   }
    373 
    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 
    381   void VisitLoadString(HLoadString* load_string) OVERRIDE {
    382     StartAttributeStream("load_kind") << load_string->GetLoadKind();
    383   }
    384 
    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 
    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 
    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 
    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 
    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 
    417   void VisitInvokeUnresolved(HInvokeUnresolved* invoke) OVERRIDE {
    418     VisitInvoke(invoke);
    419     StartAttributeStream("invoke_type") << invoke->GetOriginalInvokeType();
    420   }
    421 
    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 
    431   void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE {
    432     VisitInvoke(invoke);
    433     StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
    434   }
    435 
    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 
    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 
    450   void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
    451     StartAttributeStream("field_type") << field_access->GetFieldType();
    452   }
    453 
    454   void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) OVERRIDE {
    455     StartAttributeStream("field_type") << field_access->GetFieldType();
    456   }
    457 
    458   void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) OVERRIDE {
    459     StartAttributeStream("field_type") << field_access->GetFieldType();
    460   }
    461 
    462   void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) OVERRIDE {
    463     StartAttributeStream("field_type") << field_access->GetFieldType();
    464   }
    465 
    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)
    471   void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE {
    472     StartAttributeStream("kind") << instruction->GetOpKind();
    473   }
    474 
    475   void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE {
    476     StartAttributeStream("kind") << instruction->GetOpKind();
    477   }
    478 #endif
    479 
    480 #ifdef ART_ENABLE_CODEGEN_arm64
    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 
    489   bool IsPass(const char* name) {
    490     return strcmp(pass_name_, name) == 0;
    491   }
    492 
    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 
    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 
    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 
    646   void DumpEndOfDisassemblyBlock() {
    647     EndTag("HIR");
    648     EndTag("block");
    649   }
    650 
    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 
    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 
    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 
    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 
    769 HGraphVisualizer::HGraphVisualizer(std::ostream* output,
    770                                    HGraph* graph,
    771                                    const CodeGenerator& codegen)
    772   : output_(output), graph_(graph), codegen_(codegen) {}
    773 
    774 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 
    785 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 
    800 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