Optimizing: Add debugging output for HInstruction.
Allow printing individual instruction and its arguments with
the HGraphVisualizer. Arguments are dumped "recursively"
(but implemented with a queue instead of actual recursion).
For example, printing the Return instruction from the method
Main.testLoop17 in 530-checker-lse yields
v28 Return [i27] dex_pc:23 loop:none
i27 Add [i24,i26] dex_pc:22 loop:none
i24 Phi [i5,i15] dex_pc:n/a reg:0 is_catch_phi:false loop:none
i5 IntConstant dex_pc:0 1 loop:none
i15 IntConstant dex_pc:5 2 loop:none
i26 InstanceFieldGet [l6] dex_pc:20 field_name:TestClass.i field_type:Int32 loop:none
l6 NullCheck [l1] dex_pc:1 env:[[i5,_,_,l1,i2]] loop:none
l1 ParameterValue dex_pc:n/a loop:none
Test: Manual; modify LSE to print the instruction above.
Change-Id: Iaf41ba62cd6a5a36236ad0abca082ebffcf6a20e
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f9c63c4..44a64c1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -195,7 +195,7 @@
const char* pass_name,
bool is_after_pass,
bool graph_in_bad_state,
- const CodeGenerator& codegen,
+ const CodeGenerator* codegen,
const DisassemblyInformation* disasm_info = nullptr)
: HGraphDelegateVisitor(graph),
output_(output),
@@ -206,10 +206,10 @@
disasm_info_(disasm_info),
disassembler_(disasm_info_ != nullptr
? new HGraphVisualizerDisassembler(
- codegen_.GetInstructionSet(),
- codegen_.GetAssembler().CodeBufferBaseAddress(),
- codegen_.GetAssembler().CodeBufferBaseAddress()
- + codegen_.GetAssembler().CodeSize())
+ codegen_->GetInstructionSet(),
+ codegen_->GetAssembler().CodeBufferBaseAddress(),
+ codegen_->GetAssembler().CodeBufferBaseAddress()
+ + codegen_->GetAssembler().CodeSize())
: nullptr),
indent_(0) {}
@@ -298,10 +298,11 @@
}
void DumpLocation(std::ostream& stream, const Location& location) {
+ DCHECK(codegen_ != nullptr);
if (location.IsRegister()) {
- codegen_.DumpCoreRegister(stream, location.reg());
+ codegen_->DumpCoreRegister(stream, location.reg());
} else if (location.IsFpuRegister()) {
- codegen_.DumpFloatingPointRegister(stream, location.reg());
+ codegen_->DumpFloatingPointRegister(stream, location.reg());
} else if (location.IsConstant()) {
stream << "#";
HConstant* constant = location.GetConstant();
@@ -321,13 +322,13 @@
} else if (location.IsStackSlot()) {
stream << location.GetStackIndex() << "(sp)";
} else if (location.IsFpuRegisterPair()) {
- codegen_.DumpFloatingPointRegister(stream, location.low());
+ codegen_->DumpFloatingPointRegister(stream, location.low());
stream << "|";
- codegen_.DumpFloatingPointRegister(stream, location.high());
+ codegen_->DumpFloatingPointRegister(stream, location.high());
} else if (location.IsRegisterPair()) {
- codegen_.DumpCoreRegister(stream, location.low());
+ codegen_->DumpCoreRegister(stream, location.low());
stream << "|";
- codegen_.DumpCoreRegister(stream, location.high());
+ codegen_->DumpCoreRegister(stream, location.high());
} else if (location.IsUnallocated()) {
stream << "unallocated";
} else if (location.IsDoubleStackSlot()) {
@@ -837,6 +838,12 @@
Flush();
}
+ void Run(HInstruction* instruction) {
+ output_ << DataType::TypeId(instruction->GetType()) << instruction->GetId() << " ";
+ PrintInstruction(instruction);
+ Flush();
+ }
+
void VisitBasicBlock(HBasicBlock* block) override {
StartTag("block");
PrintProperty("name", "B", block->GetBlockId());
@@ -895,7 +902,7 @@
const char* pass_name_;
const bool is_after_pass_;
const bool graph_in_bad_state_;
- const CodeGenerator& codegen_;
+ const CodeGenerator* codegen_;
const DisassemblyInformation* disasm_info_;
std::unique_ptr<HGraphVisualizerDisassembler> disassembler_;
size_t indent_;
@@ -905,7 +912,7 @@
HGraphVisualizer::HGraphVisualizer(std::ostream* output,
HGraph* graph,
- const CodeGenerator& codegen)
+ const CodeGenerator* codegen)
: output_(output), graph_(graph), codegen_(codegen) {}
void HGraphVisualizer::PrintHeader(const char* method_name) const {
@@ -956,9 +963,21 @@
/* is_after_pass= */ true,
/* graph_in_bad_state= */ false,
codegen_,
- codegen_.GetDisassemblyInformation());
+ codegen_->GetDisassemblyInformation());
printer.Run();
}
}
+void HGraphVisualizer::DumpInstruction(std::ostream* output,
+ HGraph* graph,
+ HInstruction* instruction) {
+ HGraphVisualizerPrinter printer(graph,
+ *output,
+ /* pass_name= */ "debug",
+ /* is_after_pass= */ false,
+ /* graph_in_bad_state= */ false,
+ /* codegen= */ nullptr);
+ printer.Run(instruction);
+}
+
} // namespace art
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index e01d03c..b83e887 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -101,7 +101,7 @@
public:
HGraphVisualizer(std::ostream* output,
HGraph* graph,
- const CodeGenerator& codegen);
+ const CodeGenerator* codegen);
void PrintHeader(const char* method_name) const;
void DumpGraph(const char* pass_name, bool is_after_pass, bool graph_in_bad_state) const;
@@ -112,10 +112,12 @@
// method attributes is used. Such empty blocks don't break the c1visualizer parser.
static std::string InsertMetaDataAsCompilationBlock(const std::string& meta_data);
+ static void DumpInstruction(std::ostream* output, HGraph* graph, HInstruction* instruction);
+
private:
std::ostream* const output_;
HGraph* const graph_;
- const CodeGenerator& codegen_;
+ const CodeGenerator* codegen_;
DISALLOW_COPY_AND_ASSIGN(HGraphVisualizer);
};
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index e3e4589..d57eaf0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1312,6 +1312,44 @@
orig_instr->FixUpUserRecordsAfterEnvUseRemoval(before_use_node);
}
+std::ostream& HInstruction::Dump(std::ostream& os, bool dump_args) {
+ HGraph* graph = GetBlock()->GetGraph();
+ HGraphVisualizer::DumpInstruction(&os, graph, this);
+ if (dump_args) {
+ // Allocate memory from local ScopedArenaAllocator.
+ ScopedArenaAllocator allocator(graph->GetArenaStack());
+ // Instructions that we already visited. We print each instruction only once.
+ ArenaBitVector visited(
+ &allocator, graph->GetCurrentInstructionId(), /* expandable= */ false, kArenaAllocMisc);
+ visited.ClearAllBits();
+ visited.SetBit(GetId());
+ // Keep a queue of instructions with their indentations.
+ ScopedArenaDeque<std::pair<HInstruction*, size_t>> queue(allocator.Adapter(kArenaAllocMisc));
+ auto add_args = [&queue](HInstruction* instruction, size_t indentation) {
+ for (HInstruction* arg : ReverseRange(instruction->GetInputs())) {
+ queue.emplace_front(arg, indentation);
+ }
+ };
+ add_args(this, /*indentation=*/ 1u);
+ while (!queue.empty()) {
+ HInstruction* instruction;
+ size_t indentation;
+ std::tie(instruction, indentation) = queue.front();
+ queue.pop_front();
+ if (!visited.IsBitSet(instruction->GetId())) {
+ visited.SetBit(instruction->GetId());
+ os << '\n';
+ for (size_t i = 0; i != indentation; ++i) {
+ os << " ";
+ }
+ HGraphVisualizer::DumpInstruction(&os, graph, instruction);
+ add_args(instruction, indentation + 1u);
+ }
+ }
+ }
+ return os;
+}
+
HInstruction* HInstruction::GetNextDisregardingMoves() const {
HInstruction* next = GetNext();
while (next != nullptr && next->IsParallelMove()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index d15e40a..125f86b 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2145,6 +2145,7 @@
virtual ~HInstruction() {}
+ std::ostream& Dump(std::ostream& os, bool dump_args = false);
HInstruction* GetNext() const { return next_; }
HInstruction* GetPrevious() const { return previous_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b7e9d63..ac241aa 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -117,7 +117,7 @@
visualizer_oss_(),
visualizer_output_(visualizer_output),
visualizer_enabled_(!compiler_options.GetDumpCfgFileName().empty()),
- visualizer_(&visualizer_oss_, graph, *codegen),
+ visualizer_(&visualizer_oss_, graph, codegen),
codegen_(codegen),
visualizer_dump_mutex_(dump_mutex),
graph_in_bad_state_(false) {