Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  graph_visualizer.cc

  Sprache: C
 

/*
 * 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 <dlfcn.h>

#include <cctype>
#include <ios>
#include <sstream>

#include "android-base/stringprintf.h"
#include "art_method.h"
#include "art_method-inl.h"
#include "base/intrusive_forward_list.h"
#include "bounds_check_elimination.h"
#include "builder.h"
#include "code_generator.h"
#include "data_type-inl.h"
#include "dead_code_elimination.h"
#include "dex/descriptors_names.h"
#include "disassembler.h"
#include "inliner.h"
#include "licm.h"
#include "nodes.h"
#include "optimization.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
#include "scoped_thread_state_change-inl.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"

namespace art HIDDEN {

// Unique pass-name to identify that the dump is for printing to log.
constexpr const char* kDebugDumpName = "debug";
constexpr const char* kDebugDumpGraphName = "debug_graph";

using android::base::StringPrintf;

static bool HasWhitespace(const char* str) {
  DCHECK(str != nullptr);
  while (str[0] != 0) {
    if (isspace(str[0])) {
      return true;
    }
    str++;
  }
  return false;
}

class StringList {
 public:
  enum Format {
    kArrayBrackets,
    kSetBrackets,
  };

  // Create an empty list
  explicit StringList(Format format = kArrayBrackets) : format_(format), is_empty_(true) {}

  // Construct StringList from a linked list. List element class T
  // must provide methods `GetNext` and `Dump`.
  template<class T>
  explicit StringList(T* first_entry, Format format = kArrayBrackets) : StringList(format) {
    for (T* current = first_entry; current != nullptr; current = current->GetNext()) {
      current->Dump(NewEntryStream());
    }
  }
  // Construct StringList from a list of elements. The value type must provide method `Dump`.
  template <typename Container>
  explicit StringList(const Container& list, Format format = kArrayBrackets) : StringList(format) {
    for (const typename Container::value_type& current : list) {
      current.Dump(NewEntryStream());
    }
  }

  std::ostream& NewEntryStream() {
    if (is_empty_) {
      is_empty_ = false;
    } else {
      sstream_ << ",";
    }
    return sstream_;
  }

 private:
  Format format_;
  bool is_empty_;
  std::ostringstream sstream_;

  friend std::ostream& operator<<(std::ostream& os, const StringList& list);
};

std::ostream& operator<<(std::ostream& os, const StringList& list) {
  switch (list.format_) {
    case StringList::kArrayBrackets: return os << "[" << list.sstream_.str() << "]";
    case StringList::kSetBrackets:   return os << "{" << list.sstream_.str() << "}";
  }
}

// On target: load `libart-disassembler` only when required (to save on memory).
// On host: `libart-disassembler` should be linked directly (either as a static or dynamic lib)
#ifdef ART_TARGET
using create_disasm_prototype = Disassembler*(InstructionSet, DisassemblerOptions*);
#endif

class HGraphVisualizerDisassembler {
 public:
  HGraphVisualizerDisassembler(InstructionSet instruction_set,
                               const uint8_t* base_address,
                               const uint8_t* end_address)
      : instruction_set_(instruction_set), disassembler_(nullptr) {
#ifdef ART_TARGET
    constexpr const char* libart_disassembler_so_name =
        kIsDebugBuild ? "libartd-disassembler.so" : "libart-disassembler.so";
    libart_disassembler_handle_ = dlopen(libart_disassembler_so_name, RTLD_NOW);
    if (libart_disassembler_handle_ == nullptr) {
      LOG(ERROR) << "Failed to dlopen " << libart_disassembler_so_name << ": " << dlerror();
      return;
    }
    constexpr const char* create_disassembler_symbol = "create_disassembler";
    create_disasm_prototype* create_disassembler = reinterpret_cast<create_disasm_prototype*>(
        dlsym(libart_disassembler_handle_, create_disassembler_symbol));
    if (create_disassembler == nullptr) {
      LOG(ERROR) << "Could not find " << create_disassembler_symbol << " entry in "
                 << libart_disassembler_so_name << ": " << dlerror();
      return;
    }
#endif
    // Reading the disassembly from 0x0 is easier, so we print relative
    // addresses. We will only disassemble the code once everything has
    // been generated, so we can read data in literal pools.
    disassembler_ = std::unique_ptr<Disassembler>(create_disassembler(
            instruction_set,
            new DisassemblerOptions(/* absolute_addresses= */ false,
                                    base_address,
                                    end_address,
                                    /* can_read_literals= */ true,
                                    Is64BitInstructionSet(instruction_set)
                                        ? &Thread::DumpThreadOffset<PointerSize::k64>
                                        : &Thread::DumpThreadOffset<PointerSize::k32>)));
  }

  ~HGraphVisualizerDisassembler() {
    // We need to call ~Disassembler() before we close the library.
    disassembler_.reset();
#ifdef ART_TARGET
    if (libart_disassembler_handle_ != nullptr) {
      dlclose(libart_disassembler_handle_);
    }
#endif
  }

  void Disassemble(std::ostream& output, size_t start, size_t end) const {
    if (disassembler_ == nullptr) {
      return;
    }

    const uint8_t* base = disassembler_->GetDisassemblerOptions()->base_address_;
    if (instruction_set_ == InstructionSet::kThumb2) {
      // ARM and Thumb-2 use the same disassembler. The bottom bit of the
      // address is used to distinguish between the two.
      base += 1;
    }
    disassembler_->Dump(output, base + start, base + end);
  }

 private:
  InstructionSet instruction_set_;
  std::unique_ptr<Disassembler> disassembler_;

#ifdef ART_TARGET
  void* libart_disassembler_handle_;
#endif
};


/**
 * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra.
 */

class HGraphVisualizerPrinter final : public CRTPGraphVisitor<HGraphVisualizerPrinter> {
 public:
  HGraphVisualizerPrinter(HGraph* graph,
                          std::ostream& output,
                          const char* pass_name,
                          bool is_after_pass,
                          bool graph_in_bad_state,
                          const CodeGenerator* codegen,
                          const BlockNamer& namer,
                          const DisassemblyInformation* disasm_info = nullptr)
      : CRTPGraphVisitor(graph),
        output_(output),
        pass_name_(pass_name),
        is_after_pass_(is_after_pass),
        graph_in_bad_state_(graph_in_bad_state),
        codegen_(codegen),
        disasm_info_(disasm_info),
        namer_(namer),
        disassembler_(disasm_info_ != nullptr
                      ? new HGraphVisualizerDisassembler(
                            codegen_->GetInstructionSet(),
                            codegen_->GetAssembler().CodeBufferBaseAddress(),
                            codegen_->GetAssembler().CodeBufferBaseAddress()
                                + codegen_->GetAssembler().CodeSize())
                      : nullptr),
        indent_(0) {}

  void Flush() {
    // We use "\n" instead of std::endl to avoid implicit flushing which
    // generates too many syscalls during debug-GC tests (b/27826765).
    output_ << std::flush;
  }

  void StartTag(const char* name) {
    AddIndent();
    output_ << "begin_" << name << "\n";
    indent_++;
  }

  void EndTag(const char* name) {
    indent_--;
    AddIndent();
    output_ << "end_" << name << "\n";
  }

  void PrintProperty(const char* name, HBasicBlock* blk) {
    AddIndent();
    output_ << name << " \"" << namer_.GetName(blk) << "\"\n";
  }

  void PrintProperty(const char* name, const char* property) {
    AddIndent();
    output_ << name << " \"" << property << "\"\n";
  }

  void PrintProperty(const char* name, const char* property, int id) {
    AddIndent();
    output_ << name << " \"" << property << id << "\"\n";
  }

  void PrintEmptyProperty(const char* name) {
    AddIndent();
    output_ << name << "\n";
  }

  void PrintTime(const char* name) {
    AddIndent();
    output_ << name << " " << time(nullptr) << "\n";
  }

  void PrintInt(const char* name, int value) {
    AddIndent();
    output_ << name << " " << value << "\n";
  }

  void AddIndent() {
    for (size_t i = 0; i < indent_; ++i) {
      output_ << "  ";
    }
  }

  void PrintPredecessors(HBasicBlock* block) {
    AddIndent();
    output_ << "predecessors";
    for (HBasicBlock* predecessor : block->GetPredecessors()) {
      output_ << " \"" << namer_.GetName(predecessor) << "\" ";
    }
    if (GetGraph()->IsEntryBlock(block) && (disasm_info_ != nullptr)) {
      output_ << " \"" << kDisassemblyBlockFrameEntry << "\" ";
    }
    output_<< "\n";
  }

  void PrintSuccessors(HBasicBlock* block) {
    AddIndent();
    output_ << "successors";
    for (HBasicBlock* successor : block->GetNormalSuccessors()) {
      output_ << " \"" << namer_.GetName(successor) << "\" ";
    }
    output_<< "\n";
  }

  void PrintExceptionHandlers(HBasicBlock* block) {
    bool has_slow_paths = GetGraph()->IsExitBlock(block) &&
                          (disasm_info_ != nullptr) &&
                          !disasm_info_->GetSlowPathIntervals().empty();
    if (IsDebugDump() && block->GetExceptionalSuccessors().empty() && !has_slow_paths) {
      return;
    }
    AddIndent();
    output_ << "xhandlers";
    for (HBasicBlock* handler : block->GetExceptionalSuccessors()) {
      output_ << " \"" << namer_.GetName(handler) << "\" ";
    }
    if (has_slow_paths) {
      output_ << " \"" << kDisassemblyBlockSlowPaths << "\" ";
    }
    output_<< "\n";
  }

  void DumpLocation(std::ostream& stream, const Location& location) {
    DCHECK(codegen_ != nullptr);
    if (location.IsCoreRegister()) {
      codegen_->DumpCoreRegister(stream, location.reg());
    } else if (location.IsFpuRegister()) {
      codegen_->DumpFloatingPointRegister(stream, location.reg());
    } else if (location.IsConstant()) {
      stream << "#";
      HConstant* constant = location.GetConstant();
      if (constant->IsIntConstant()) {
        stream << constant->AsIntConstant()->GetValue();
      } else if (constant->IsLongConstant()) {
        stream << constant->AsLongConstant()->GetValue();
      } else if (constant->IsFloatConstant()) {
        stream << constant->AsFloatConstant()->GetValue();
      } else if (constant->IsDoubleConstant()) {
        stream << constant->AsDoubleConstant()->GetValue();
      } else if (constant->IsNullConstant()) {
        stream << "null";
      }
    } else if (location.IsInvalid()) {
      stream << "invalid";
    } else if (location.IsStackSlot()) {
      stream << location.GetStackIndex() << "(sp)";
    } else if (location.IsFpuRegisterPair()) {
      codegen_->DumpFloatingPointRegister(stream, location.low());
      stream << "|";
      codegen_->DumpFloatingPointRegister(stream, location.high());
    } else if (location.IsCoreRegisterPair()) {
      codegen_->DumpCoreRegister(stream, location.low());
      stream << "|";
      codegen_->DumpCoreRegister(stream, location.high());
    } else if (location.IsUnallocated()) {
      stream << "unallocated";
    } else if (location.IsDoubleStackSlot()) {
      stream << "2x" << location.GetStackIndex() << "(sp)";
    } else {
      DCHECK(location.IsSIMDStackSlot());
      stream << "4x" << location.GetStackIndex() << "(sp)";
    }
  }

  std::ostream& StartAttributeStream(const char* name = nullptr) {
    if (name == nullptr) {
      output_ << " ";
    } else {
      DCHECK(!HasWhitespace(name)) << "Checker does not allow spaces in attributes";
      output_ << " " << name << ":";
    }
    return output_;
  }

  void VisitParallelMove(HParallelMove* instruction) {
    StartAttributeStream("liveness") << instruction->GetLifetimePosition();
    StringList moves;
    for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
      MoveOperands* move = instruction->MoveOperandsAt(i);
      std::ostream& str = moves.NewEntryStream();
      DumpLocation(str, move->GetSource());
      str << "->";
      DumpLocation(str, move->GetDestination());
    }
    StartAttributeStream("moves") << moves;
  }

  void VisitParameterValue(HParameterValue* instruction) {
    StartAttributeStream("is_this") << std::boolalpha << instruction->IsThis() << std::noboolalpha;
  }

  void VisitIntConstant(HIntConstant* instruction) {
    StartAttributeStream() << instruction->GetValue();
  }

  void VisitLongConstant(HLongConstant* instruction) {
    StartAttributeStream() << instruction->GetValue();
  }

  void VisitFloatConstant(HFloatConstant* instruction) {
    StartAttributeStream() << instruction->GetValue();
  }

  void VisitDoubleConstant(HDoubleConstant* instruction) {
    StartAttributeStream() << instruction->GetValue();
  }

  void VisitPhi(HPhi* phi) {
    StartAttributeStream("reg") << phi->GetRegNumber();
    StartAttributeStream("is_catch_phi") << std::boolalpha << phi->IsCatchPhi() << std::noboolalpha;
    StartAttributeStream("is_live") << std::boolalpha << phi->IsLive() << std::noboolalpha;
  }

  void VisitMemoryBarrier(HMemoryBarrier* barrier) {
    StartAttributeStream("kind") << barrier->GetBarrierKind();
  }

  void VisitMonitorOperation(HMonitorOperation* monitor) {
    StartAttributeStream("kind") << (monitor->IsEnter() ? "enter" : "exit");
  }

  void VisitLoadClass(HLoadClass* load_class) {
    StartAttributeStream("load_kind") << load_class->GetLoadKind();
    StartAttributeStream("in_image") << std::boolalpha << load_class->IsInImage();
    StartAttributeStream("class_name")
        << load_class->GetDexFile().PrettyType(load_class->GetTypeIndex());
    StartAttributeStream("gen_clinit_check")
        << std::boolalpha << load_class->MustGenerateClinitCheck() << std::noboolalpha;
    StartAttributeStream("needs_access_check") << std::boolalpha
        << load_class->NeedsAccessCheck() << std::noboolalpha;
  }

  void VisitClinitCheck(HClinitCheck* clinit_check) {
    HLoadClass* load_class = clinit_check->GetLoadClass();
    StartAttributeStream("class_name")
        << load_class->GetDexFile().PrettyType(load_class->GetTypeIndex());
  }

  void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) {
    StartAttributeStream("load_kind") << "RuntimeCall";
    StartAttributeStream("method_handle_index") << load_method_handle->GetMethodHandleIndex();
  }

  void VisitLoadMethodType(HLoadMethodType* load_method_type) {
    StartAttributeStream("load_kind") << "RuntimeCall";
    const DexFile& dex_file = load_method_type->GetDexFile();
    if (dex_file.NumProtoIds() >= load_method_type->GetProtoIndex().index_) {
      const dex::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
      StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
    } else {
      StartAttributeStream("method_type")
          << "<<Unknown proto-idx: " << load_method_type->GetProtoIndex() << ">>";
    }
  }

  void VisitLoadString(HLoadString* load_string) {
    StartAttributeStream("load_kind") << load_string->GetLoadKind();
  }

  void HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
    StartAttributeStream("check_kind") << check->GetTypeCheckKind();
    StartAttributeStream("must_do_null_check") << std::boolalpha
        << check->MustDoNullCheck() << std::noboolalpha;
    if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
      StartAttributeStream("path_to_root") << std::hex
          << "0x" << check->GetBitstringPathToRoot() << std::dec;
      StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec;
    }
  }

  void VisitCheckCast(HCheckCast* check_cast) {
    HandleTypeCheckInstruction(check_cast);
  }

  void VisitInstanceOf(HInstanceOf* instance_of) {
    HandleTypeCheckInstruction(instance_of);
  }

  void VisitArrayLength(HArrayLength* array_length) {
    StartAttributeStream("is_string_length") << std::boolalpha
        << array_length->IsStringLength() << std::noboolalpha;
    if (array_length->IsEmittedAtUseSite()) {
      StartAttributeStream("emitted_at_use") << "true";
    }
  }

  void VisitBoundsCheck(HBoundsCheck* bounds_check) {
    StartAttributeStream("is_string_char_at") << std::boolalpha
        << bounds_check->IsStringCharAt() << std::noboolalpha;
  }

  void VisitSuspendCheck(HSuspendCheck* suspend_check) {
    StartAttributeStream("is_no_op")
        << std::boolalpha << suspend_check->IsNoOp() << std::noboolalpha;
  }

  void VisitArrayGet(HArrayGet* array_get) {
    StartAttributeStream("is_string_char_at") << std::boolalpha
        << array_get->IsStringCharAt() << std::noboolalpha;
  }

  void VisitArraySet(HArraySet* array_set) {
    StartAttributeStream("value_can_be_null")
        << std::boolalpha << array_set->GetValueCanBeNull() << std::noboolalpha;
    StartAttributeStream("needs_type_check")
        << std::boolalpha << array_set->NeedsTypeCheck() << std::noboolalpha;
    StartAttributeStream("static_type_of_array_is_object_array")
        << std::boolalpha << array_set->StaticTypeOfArrayIsObjectArray() << std::noboolalpha;
    StartAttributeStream("can_trigger_gc")
        << std::boolalpha << array_set->GetSideEffects().Includes(SideEffects::CanTriggerGC())
        << std::noboolalpha;
    StartAttributeStream("write_barrier_kind") << array_set->GetWriteBarrierKind();
  }

  void VisitNewInstance(HNewInstance* new_instance) {
    StartAttributeStream("is_finalizable")
        << std::boolalpha << new_instance->IsFinalizable() << std::noboolalpha;
    StartAttributeStream("is_partial_materialization")
        << std::boolalpha << new_instance->IsPartialMaterialization() << std::noboolalpha;
  }

  void VisitCompare(HCompare* compare) {
    StartAttributeStream("bias") << compare->GetBias();
    StartAttributeStream("comparison_type") << compare->GetComparisonType();
  }

  void VisitCondition(HCondition* condition) {
    StartAttributeStream("bias") << condition->GetBias();
    StartAttributeStream("emitted_at_use_site")
        << std::boolalpha << condition->IsEmittedAtUseSite() << std::noboolalpha;
  }

  void VisitIf(HIf* if_instr) {
    StartAttributeStream("true_count") << if_instr->GetTrueCount();
    StartAttributeStream("false_count") << if_instr->GetFalseCount();
  }

  void VisitInvoke(HInvoke* invoke) {
    StartAttributeStream("dex_file_index") << invoke->GetMethodReference().index;
    ArtMethod* method = invoke->GetResolvedMethod();
    // We don't print signatures, which conflict with c1visualizer format.
    static constexpr bool kWithSignature = false;
    // Note that we can only use the graph's dex file for the unresolved case. The
    // other invokes might be coming from inlined methods.
    ScopedObjectAccess soa(Thread::Current());
    std::string method_name = (method == nullptr)
        ? invoke->GetMethodReference().PrettyMethod(kWithSignature)
        : method->PrettyMethod(kWithSignature);
    StartAttributeStream("method_name") << method_name;
    StartAttributeStream("always_throws") << std::boolalpha
                                          << invoke->AlwaysThrows()
                                          << std::noboolalpha;
    if (method != nullptr) {
      StartAttributeStream("method_index") << method->GetMethodIndex();
    }
    StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
  }

  void VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
    VisitInvoke(invoke);
    StartAttributeStream("invoke_type") << invoke->GetInvokeType();
  }

  void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
    VisitInvoke(invoke);
    StartAttributeStream("method_load_kind") << invoke->GetMethodLoadKind();
    if (invoke->IsStatic()) {
      StartAttributeStream("clinit_check") << invoke->GetClinitCheckRequirement();
    }
  }

  void VisitInvokeVirtual(HInvokeVirtual* invoke) {
    VisitInvoke(invoke);
  }

  void VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
    VisitInvoke(invoke);
    StartAttributeStream("invoke_type") << "InvokePolymorphic";
  }

  void VisitInstanceFieldGet(HInstanceFieldGet* iget) {
    StartAttributeStream("field_name") <<
        iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(),
                                                      /* with type */ false);
    StartAttributeStream("field_type") << iget->GetFieldType();
  }

  void VisitInstanceFieldSet(HInstanceFieldSet* iset) {
    StartAttributeStream("field_name") <<
        iset->GetFieldInfo().GetDexFile().PrettyField(iset->GetFieldInfo().GetFieldIndex(),
                                                      /* with type */ false);
    StartAttributeStream("field_type") << iset->GetFieldType();
    StartAttributeStream("write_barrier_kind") << iset->GetWriteBarrierKind();
    StartAttributeStream("value_can_be_null")
        << std::boolalpha << iset->GetValueCanBeNull() << std::noboolalpha;
  }

  void VisitStaticFieldGet(HStaticFieldGet* sget) {
    StartAttributeStream("field_name") <<
        sget->GetFieldInfo().GetDexFile().PrettyField(sget->GetFieldInfo().GetFieldIndex(),
                                                      /* with type */ false);
    StartAttributeStream("field_type") << sget->GetFieldType();
  }

  void VisitStaticFieldSet(HStaticFieldSet* sset) {
    StartAttributeStream("field_name") <<
        sset->GetFieldInfo().GetDexFile().PrettyField(sset->GetFieldInfo().GetFieldIndex(),
                                                      /* with type */ false);
    StartAttributeStream("field_type") << sset->GetFieldType();
    StartAttributeStream("write_barrier_kind") << sset->GetWriteBarrierKind();
    StartAttributeStream("value_can_be_null")
        << std::boolalpha << sset->GetValueCanBeNull() << std::noboolalpha;
  }

  void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) {
    StartAttributeStream("field_type") << field_access->GetFieldType();
  }

  void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) {
    StartAttributeStream("field_type") << field_access->GetFieldType();
  }

  void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) {
    StartAttributeStream("field_type") << field_access->GetFieldType();
  }

  void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) {
    StartAttributeStream("field_type") << field_access->GetFieldType();
  }

  void VisitTryBoundary(HTryBoundary* try_boundary) {
    StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
  }

  void VisitGoto(HGoto* instruction) {
    StartAttributeStream("target") << namer_.GetName(instruction->GetBlock()->GetSingleSuccessor());
  }

  void VisitDeoptimize(HDeoptimize* deoptimize) {
    StartAttributeStream("kind") << deoptimize->GetKind();
  }

  void VisitVecOperation(HVecOperation* vec_operation) {
    StartAttributeStream("packed_type") << vec_operation->GetPackedType();
  }

  void VisitVecMemoryOperation(HVecMemoryOperation* vec_mem_operation) {
    VisitVecOperation(vec_mem_operation);
    StartAttributeStream("alignment") << vec_mem_operation->GetAlignment().ToString();
  }

  void VisitVecHalvingAdd(HVecHalvingAdd* hadd) {
    static constexpr auto visit_super = ForwardVisit(&CRTPGraphVisitor::VisitVecHalvingAdd);
    (this->*visit_super)(hadd);
    StartAttributeStream("rounded") << std::boolalpha << hadd->IsRounded() << std::noboolalpha;
  }

  void VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
    VisitVecOperation(instruction);
    StartAttributeStream("kind") << instruction->GetOpKind();
  }

  void VisitVecDotProd(HVecDotProd* instruction) {
    VisitVecOperation(instruction);
    DataType::Type arg_type = instruction->InputAt(1)->AsVecOperation()->GetPackedType();
    StartAttributeStream("type") << (instruction->IsZeroExtending() ?
                                    DataType::ToUnsigned(arg_type) :
                                    DataType::ToSigned(arg_type));
  }

  void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
    StartAttributeStream("kind") << instruction->GetOpKind();
  }

#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
  void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) {
    StartAttributeStream("kind") << instruction->GetOpKind();
  }

  void VisitDataProcWithShifterOp(HDataProcWithShifterOp* instruction) {
    StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind();
    if (HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
      StartAttributeStream("shift") << instruction->GetShiftAmount();
    }
  }
#endif

#if defined(ART_ENABLE_CODEGEN_riscv64)
  void VisitRiscv64ShiftAdd(HRiscv64ShiftAdd* instruction) {
    StartAttributeStream("distance") << instruction->GetDistance();
  }
#endif

  bool IsPass(const char* name) {
    return strstr(pass_name_, name) == pass_name_;
  }

  bool IsDebugDump() {
    return IsPass(kDebugDumpGraphName) || IsPass(kDebugDumpName);
  }

  void PrintInstruction(HInstruction* instruction) {
    output_ << instruction->DebugName();
    HConstInputsRef inputs = instruction->GetInputs();
    if (!inputs.empty()) {
      StringList input_list;
      for (const HInstruction* input : inputs) {
        input_list.NewEntryStream() << DataType::TypeId(input->GetType()) << input->GetId();
      }
      StartAttributeStream() << input_list;
    }
    if (instruction->GetDexPc() != kNoDexPc) {
      StartAttributeStream("dex_pc") << instruction->GetDexPc();
    } else {
      StartAttributeStream("dex_pc") << "n/a";
    }
    HBasicBlock* block = instruction->GetBlock();
    StartAttributeStream("block") << namer_.GetName(block);

    Dispatch(instruction);
    if (instruction->HasEnvironment()) {
      StringList envs;
      for (HEnvironment* environment = instruction->GetEnvironment();
           environment != nullptr;
           environment = environment->GetParent()) {
        StringList vregs;
        for (size_t i = 0, e = environment->Size(); i < e; ++i) {
          HInstruction* insn = environment->GetInstructionAt(i);
          if (insn != nullptr) {
            vregs.NewEntryStream() << DataType::TypeId(insn->GetType()) << insn->GetId();
          } else {
            vregs.NewEntryStream() << "_";
          }
        }
        envs.NewEntryStream() << vregs;
      }
      StartAttributeStream("env") << envs;
    }
    if (IsPass(SsaLivenessAnalysis::kLivenessPassName)
        && is_after_pass_
        && instruction->GetLifetimePosition() != kNoLifetime) {
      StartAttributeStream("liveness") << instruction->GetLifetimePosition();
      if (instruction->HasLiveInterval()) {
        LiveInterval* interval = instruction->GetLiveInterval();
        StartAttributeStream("ranges")
            << StringList(interval->GetFirstRange(), StringList::kSetBrackets);
        StartAttributeStream("uses") << StringList(interval->GetUses());
        StartAttributeStream("env_uses") << StringList(interval->GetEnvironmentUses());
        StartAttributeStream("is_fixed") << interval->IsFixed();
        StartAttributeStream("is_split") << interval->IsSplit();
        StartAttributeStream("is_pair") << interval->IsPair();
      }
    }

    if (IsPass(RegisterAllocator::kRegisterAllocatorPassName) && is_after_pass_) {
      StartAttributeStream("liveness") << instruction->GetLifetimePosition();
      LocationSummary* locations = instruction->GetLocations();
      if (locations != nullptr) {
        StringList input_list;
        for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
          DumpLocation(input_list.NewEntryStream(), locations->InAt(i));
        }
        std::ostream& attr = StartAttributeStream("locations");
        attr << input_list << "->";
        DumpLocation(attr, locations->Out());
      }
    }

    HLoopInformation* loop_info = (block != nullptr) ? block->GetLoopInformation() : nullptr;
    if (loop_info == nullptr) {
      StartAttributeStream("loop") << "none";
    } else {
      StartAttributeStream("loop") << namer_.GetName(loop_info->GetHeader());
      HLoopInformation* outer = loop_info->GetPreHeader()->GetLoopInformation();
      if (outer != nullptr) {
        StartAttributeStream("outer_loop") << namer_.GetName(outer->GetHeader());
      } else {
        StartAttributeStream("outer_loop") << "none";
      }
      StartAttributeStream("irreducible")
          << std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha;
    }

    // For the builder and the inliner, we want to add extra information on HInstructions
    // that have reference types, and also HInstanceOf/HCheckcast.
    if ((IsPass(HGraphBuilder::kBuilderPassName)
        || IsPass(ReferenceTypePropagation::kReferenceTypePropagationPassName)
        || IsPass(HInliner::kInlinerPassName)
        || IsDebugDump())
        && (instruction->GetType() == DataType::Type::kReference ||
            instruction->IsInstanceOf() ||
            instruction->IsCheckCast())) {
      ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference)
          ? instruction->IsLoadClass()
              ? instruction->AsLoadClass()->GetLoadedClassRTI()
              : instruction->GetReferenceTypeInfo()
          : instruction->IsInstanceOf()
              ? instruction->AsInstanceOf()->GetTargetClassRTI()
              : instruction->AsCheckCast()->GetTargetClassRTI();
      ScopedObjectAccess soa(Thread::Current());
      if (info.IsValid()) {
        StartAttributeStream("klass")
            << mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get());
        if (instruction->GetType() == DataType::Type::kReference) {
          StartAttributeStream("can_be_null")
              << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
        }
        StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
      } else if (instruction->IsLoadClass() ||
                 instruction->IsInstanceOf() ||
                 instruction->IsCheckCast()) {
        StartAttributeStream("klass") << "unresolved";
      } else {
        StartAttributeStream("klass") << "invalid";
      }
    }
    if (disasm_info_ != nullptr) {
      DCHECK(disassembler_ != nullptr);
      // If the information is available, disassemble the code generated for
      // this instruction.
      auto it = disasm_info_->GetInstructionIntervals().find(instruction);
      if (it != disasm_info_->GetInstructionIntervals().end()
          && it->second.start != it->second.end) {
        output_ << "\n";
        disassembler_->Disassemble(output_, it->second.start, it->second.end);
      }
    }
  }

  void PrintInstructions(const HInstructionList& list) {
    for (HInstructionIteratorPrefetchNext it(list); !it.Done(); it.Advance()) {
      HInstruction* instruction = it.Current();
      int bci = 0;
      size_t num_uses = instruction->GetUses().SizeSlow();
      AddIndent();
      output_ << bci << " " << num_uses << " "
              << DataType::TypeId(instruction->GetType()) << instruction->GetId() << " ";
      PrintInstruction(instruction);
      output_ << " " << kEndInstructionMarker << "\n";
    }
  }

  void DumpStartOfDisassemblyBlock(const char* block_name,
                                   int predecessor_index,
                                   int successor_index) {
    StartTag("block");
    PrintProperty("name", block_name);
    PrintInt("from_bci", -1);
    PrintInt("to_bci", -1);
    if (predecessor_index != -1) {
      PrintProperty("predecessors""B", predecessor_index);
    } else {
      PrintEmptyProperty("predecessors");
    }
    if (successor_index != -1) {
      PrintProperty("successors""B", successor_index);
    } else {
      PrintEmptyProperty("successors");
    }
    PrintEmptyProperty("xhandlers");
    PrintEmptyProperty("flags");
    StartTag("states");
    StartTag("locals");
    PrintInt("size"0);
    PrintProperty("method""None");
    EndTag("locals");
    EndTag("states");
    StartTag("HIR");
  }

  void DumpEndOfDisassemblyBlock() {
    EndTag("HIR");
    EndTag("block");
  }

  void DumpDisassemblyBlockForFrameEntry() {
    DumpStartOfDisassemblyBlock(kDisassemblyBlockFrameEntry,
                                -1,
                                GetGraph()->GetEntryBlock()->GetBlockId());
    output_ << "    0 0 disasm " << kDisassemblyBlockFrameEntry << " ";
    GeneratedCodeInterval frame_entry = disasm_info_->GetFrameEntryInterval();
    if (frame_entry.start != frame_entry.end) {
      output_ << "\n";
      disassembler_->Disassemble(output_, frame_entry.start, frame_entry.end);
    }
    output_ << kEndInstructionMarker << "\n";
    DumpEndOfDisassemblyBlock();
  }

  void DumpDisassemblyBlockForSlowPaths() {
    if (disasm_info_->GetSlowPathIntervals().empty()) {
      return;
    }
    // If the graph has an exit block we attach the block for the slow paths
    // after it. Else we just add the block to the graph without linking it to
    // any other.
    DumpStartOfDisassemblyBlock(
        kDisassemblyBlockSlowPaths,
        GetGraph()->HasExitBlock() ? GetGraph()->GetExitBlock()->GetBlockId() : -1,
        -1);
    for (SlowPathCodeInfo info : disasm_info_->GetSlowPathIntervals()) {
      output_ << "    0 0 disasm " << info.slow_path->GetDescription() << "\n";
      disassembler_->Disassemble(output_, info.code_interval.start, info.code_interval.end);
      output_ << kEndInstructionMarker << "\n";
    }
    DumpEndOfDisassemblyBlock();
  }

  void Run() {
    StartTag("cfg");
    std::ostringstream oss;
    oss << pass_name_;
    if (!IsDebugDump()) {
      oss << " (" << (GetGraph()->IsCompilingBaseline() ? "baseline " : "")
          << (is_after_pass_ ? "after" : "before")
          << (graph_in_bad_state_ ? ", bad_state" : "") << ")";
    }
    PrintProperty("name", oss.str().c_str());
    if (disasm_info_ != nullptr) {
      DumpDisassemblyBlockForFrameEntry();
    }
    VisitInsertionOrder();
    if (disasm_info_ != nullptr) {
      DumpDisassemblyBlockForSlowPaths();
    }
    EndTag("cfg");
    Flush();
  }

  void Run(HInstruction* instruction) {
    output_ << DataType::TypeId(instruction->GetType()) << instruction->GetId() << " ";
    PrintInstruction(instruction);
    Flush();
  }

  void VisitBasicBlock(HBasicBlock* block) {
    StartTag("block");
    PrintProperty("name", block);
    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 if (!IsDebugDump()) {
      // Don't print useless information to logcat.
      PrintInt("from_bci", -1);
      PrintInt("to_bci", -1);
    }
    PrintPredecessors(block);
    PrintSuccessors(block);
    PrintExceptionHandlers(block);

    if (block->IsCatchBlock()) {
      PrintProperty("flags""catch_block");
    } else if (block->IsTryBlock()) {
      std::stringstream flags_properties;
      flags_properties << "try_start "
                       << namer_.GetName(block->GetTryCatchInformation()->GetTryEntry().GetBlock());
      PrintProperty("flags", flags_properties.str().c_str());
    } else if (!IsDebugDump()) {
      // Don't print useless information to logcat
      PrintEmptyProperty("flags");
    }

    if (block->GetDominator() != nullptr) {
      PrintProperty("dominator", block->GetDominator());
    }

    if (!IsDebugDump() || !block->GetPhis().IsEmpty()) {
      StartTag("states");
      StartTag("locals");
      PrintInt("size"0);
      PrintProperty("method""None");
      for (HInstructionIteratorPrefetchNext it(block->GetPhis()); !it.Done(); it.Advance()) {
        AddIndent();
        HInstruction* instruction = it.Current();
        output_ << instruction->GetId() << " " << DataType::TypeId(instruction->GetType())
                << instruction->GetId() << "[ ";
        for (const HInstruction* input : instruction->GetInputs()) {
          output_ << input->GetId() << " ";
        }
        output_ << "]\n";
      }
      EndTag("locals");
      EndTag("states");
    }

    StartTag("HIR");
    PrintInstructions(block->GetPhis());
    PrintInstructions(block->GetInstructions());
    EndTag("HIR");
    EndTag("block");
  }

  static constexpr const charconst kEndInstructionMarker = "<|@";
  static constexpr const charconst kDisassemblyBlockFrameEntry = "FrameEntry";
  static constexpr const charconst kDisassemblyBlockSlowPaths = "SlowPaths";

 private:
  std::ostream& output_;
  const char* pass_name_;
  const bool is_after_pass_;
  const bool graph_in_bad_state_;
  const CodeGenerator* codegen_;
  const DisassemblyInformation* disasm_info_;
  const BlockNamer& namer_;
  std::unique_ptr<HGraphVisualizerDisassembler> disassembler_;
  size_t indent_;

  DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter);
};

std::ostream& HGraphVisualizer::OptionalDefaultNamer::PrintName(std::ostream&&nbsp;os,
                                                                HBasicBlock* blk) const {
  if (namer_) {
    return namer_->get().PrintName(os, blk);
  } else {
    return BlockNamer::PrintName(os, blk);
  }
}

HGraphVisualizer::HGraphVisualizer(std::ostream* output,
                                   HGraph* graph,
                                   const CodeGenerator* codegen,
                                   std::optional<std::reference_wrapper<const BlockNamer>> namer)
    : output_(output), graph_(graph), codegen_(codegen), namer_(namer) {}

void HGraphVisualizer::PrintHeader(const char* method_name) const {
  DCHECK(output_ != nullptr);
  HGraphVisualizerPrinter printer(graph_, *output_, ""truefalse, codegen_, namer_);
  printer.StartTag("compilation");
  printer.PrintProperty("name", method_name);
  printer.PrintProperty("method", method_name);
  printer.PrintTime("date");
  printer.EndTag("compilation");
  printer.Flush();
}

std::string HGraphVisualizer::InsertMetaDataAsCompilationBlock(const std::string&&nbsp;meta_data) {
  std::string time_str = std::to_string(time(nullptr));
  std::string quoted_meta_data = "\"" + meta_data + "\"";
  return StringPrintf("begin_compilation\n"
                      "  name %s\n"
                      "  method %s\n"
                      "  date %s\n"
                      "end_compilation\n",
                      quoted_meta_data.c_str(),
                      quoted_meta_data.c_str(),
                      time_str.c_str());
}

void HGraphVisualizer::DumpGraphDebug() const {
  DumpGraph(/* pass_name= */ kDebugDumpGraphName,
            /* is_after_pass= */ false,
            /* graph_in_bad_state= */ true);
}

void HGraphVisualizer::DumpGraph(const char* pass_name,
                                 bool is_after_pass,
                                 bool graph_in_bad_state) const {
  DCHECK(output_ != nullptr);
  if (!graph_->GetBlocks().empty()) {
    HGraphVisualizerPrinter printer(graph_,
                                    *output_,
                                    pass_name,
                                    is_after_pass,
                                    graph_in_bad_state,
                                    codegen_,
                                    namer_);
    printer.Run();
  }
}

void HGraphVisualizer::DumpGraphWithDisassembly() const {
  DCHECK(output_ != nullptr);
  if (!graph_->GetBlocks().empty()) {
    HGraphVisualizerPrinter printer(graph_,
                                    *output_,
                                    "disassembly",
                                    /* is_after_pass= */ true,
                                    /* graph_in_bad_state= */ false,
                                    codegen_,
                                    namer_,
                                    codegen_->GetDisassemblyInformation());
    printer.Run();
  }
}

void HGraphVisualizer::DumpInstruction(std::ostream* output,
                                       HGraph* graph,
                                       HInstruction* instruction) {
  BlockNamer namer;
  HGraphVisualizerPrinter printer(graph,
                                  *output,
                                  /* pass_name= */ kDebugDumpName,
                                  /* is_after_pass= */ false,
                                  /* graph_in_bad_state= */ false,
                                  /* codegen= */ nullptr,
                                  /* namer= */ namer);
  printer.Run(instruction);
}

}  // namespace art

Messung V0.5 in Prozent
C=92 H=91 G=91

¤ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik