/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "graph_visualizer.h" #include "code_generator.h" #include "driver/dex_compilation_unit.h" #include "nodes.h" #include "ssa_liveness_analysis.h" namespace art { /** * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra. */ class HGraphVisualizerPrinter : public HGraphVisitor { public: HGraphVisualizerPrinter(HGraph* graph, std::ostream& output, const char* pass_name, const CodeGenerator& codegen) : HGraphVisitor(graph), output_(output), pass_name_(pass_name), codegen_(codegen), indent_(0) {} void StartTag(const char* name) { AddIndent(); output_ << "begin_" << name << std::endl; indent_++; } void EndTag(const char* name) { indent_--; AddIndent(); output_ << "end_" << name << std::endl; } void PrintProperty(const char* name, const char* property) { AddIndent(); output_ << name << " \"" << property << "\"" << std::endl; } void PrintProperty(const char* name, const char* property, int id) { AddIndent(); output_ << name << " \"" << property << id << "\"" << std::endl; } void PrintEmptyProperty(const char* name) { AddIndent(); output_ << name << std::endl; } void PrintTime(const char* name) { AddIndent(); output_ << name << " " << time(NULL) << std::endl; } void PrintInt(const char* name, int value) { AddIndent(); output_ << name << " " << value << std::endl; } void AddIndent() { for (size_t i = 0; i < indent_; ++i) { output_ << " "; } } void PrintPredecessors(HBasicBlock* block) { AddIndent(); output_ << "predecessors"; for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { HBasicBlock* predecessor = block->GetPredecessors().Get(i); output_ << " \"B" << predecessor->GetBlockId() << "\" "; } output_<< std::endl; } void PrintSuccessors(HBasicBlock* block) { AddIndent(); output_ << "successors"; for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) { HBasicBlock* successor = block->GetSuccessors().Get(i); output_ << " \"B" << successor->GetBlockId() << "\" "; } output_<< std::endl; } void DumpLocation(Location location, Primitive::Type type) { if (location.IsRegister()) { if (type == Primitive::kPrimDouble || type == Primitive::kPrimFloat) { codegen_.DumpFloatingPointRegister(output_, location.reg().RegId()); } else { codegen_.DumpCoreRegister(output_, location.reg().RegId()); } } else if (location.IsConstant()) { output_ << "constant"; } else if (location.IsInvalid()) { output_ << "invalid"; } else if (location.IsStackSlot()) { output_ << location.GetStackIndex() << "(sp)"; } else { DCHECK(location.IsDoubleStackSlot()); output_ << "2x" << location.GetStackIndex() << "(sp)"; } } void VisitParallelMove(HParallelMove* instruction) { output_ << instruction->DebugName(); output_ << " ("; for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) { MoveOperands* move = instruction->MoveOperandsAt(i); DumpLocation(move->GetSource(), Primitive::kPrimInt); output_ << " -> "; DumpLocation(move->GetDestination(), Primitive::kPrimInt); if (i + 1 != e) { output_ << ", "; } } output_ << ")"; } void VisitInstruction(HInstruction* instruction) { output_ << instruction->DebugName(); if (instruction->InputCount() > 0) { output_ << " [ "; for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) { output_ << "v" << inputs.Current()->GetId() << " "; } output_ << "]"; } if (pass_name_ == kLivenessPassName && instruction->GetLifetimePosition() != kNoLifetime) { output_ << " (liveness: " << instruction->GetLifetimePosition(); if (instruction->HasLiveInterval()) { output_ << " "; const LiveInterval& interval = *instruction->GetLiveInterval(); interval.Dump(output_); } output_ << ")"; } else if (pass_name_ == kRegisterAllocatorPassName) { LocationSummary* locations = instruction->GetLocations(); if (locations != nullptr) { output_ << " ( "; for (size_t i = 0; i < instruction->InputCount(); ++i) { DumpLocation(locations->InAt(i), instruction->InputAt(i)->GetType()); output_ << " "; } output_ << ")"; if (locations->Out().IsValid()) { output_ << " -> "; DumpLocation(locations->Out(), instruction->GetType()); } } } } void PrintInstructions(const HInstructionList& list) { const char* kEndInstructionMarker = "<|@"; for (HInstructionIterator it(list); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); AddIndent(); int bci = 0; output_ << bci << " " << instruction->NumberOfUses() << " v" << instruction->GetId() << " "; instruction->Accept(this); output_ << kEndInstructionMarker << std::endl; } } void Run() { StartTag("cfg"); PrintProperty("name", pass_name_); VisitInsertionOrder(); EndTag("cfg"); } void VisitBasicBlock(HBasicBlock* block) { StartTag("block"); PrintProperty("name", "B", block->GetBlockId()); if (block->GetLifetimeStart() != kNoLifetime) { // Piggy back on these fields to show the lifetime of the block. PrintInt("from_bci", block->GetLifetimeStart()); PrintInt("to_bci", block->GetLifetimeEnd()); } else { PrintInt("from_bci", -1); PrintInt("to_bci", -1); } PrintPredecessors(block); PrintSuccessors(block); PrintEmptyProperty("xhandlers"); PrintEmptyProperty("flags"); if (block->GetDominator() != nullptr) { PrintProperty("dominator", "B", block->GetDominator()->GetBlockId()); } StartTag("states"); StartTag("locals"); PrintInt("size", 0); PrintProperty("method", "None"); for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { AddIndent(); HInstruction* instruction = it.Current(); output_ << instruction->GetId() << " v" << instruction->GetId() << "[ "; for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) { output_ << inputs.Current()->GetId() << " "; } output_ << "]" << std::endl; } EndTag("locals"); EndTag("states"); StartTag("HIR"); PrintInstructions(block->GetPhis()); PrintInstructions(block->GetInstructions()); EndTag("HIR"); EndTag("block"); } private: std::ostream& output_; const char* pass_name_; const CodeGenerator& codegen_; size_t indent_; DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter); }; HGraphVisualizer::HGraphVisualizer(std::ostream* output, HGraph* graph, const char* string_filter, const CodeGenerator& codegen, const DexCompilationUnit& cu) : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) { if (output == nullptr) { return; } std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile()); if (pretty_name.find(string_filter) == std::string::npos) { return; } is_enabled_ = true; HGraphVisualizerPrinter printer(graph, *output_, "", codegen_); printer.StartTag("compilation"); printer.PrintProperty("name", pretty_name.c_str()); printer.PrintProperty("method", pretty_name.c_str()); printer.PrintTime("date"); printer.EndTag("compilation"); } HGraphVisualizer::HGraphVisualizer(std::ostream* output, HGraph* graph, const CodeGenerator& codegen, const char* name) : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) { if (output == nullptr) { return; } is_enabled_ = true; HGraphVisualizerPrinter printer(graph, *output_, "", codegen_); printer.StartTag("compilation"); printer.PrintProperty("name", name); printer.PrintProperty("method", name); printer.PrintTime("date"); printer.EndTag("compilation"); } void HGraphVisualizer::DumpGraph(const char* pass_name) { if (!is_enabled_) { return; } HGraphVisualizerPrinter printer(graph_, *output_, pass_name, codegen_); printer.Run(); } } // namespace art