From eb7b7399dbdb5e471b8ae00a567bf4f19edd3907 Mon Sep 17 00:00:00 2001 From: Alexandre Rames Date: Fri, 19 Jun 2015 14:47:01 +0100 Subject: [PATCH] Opt compiler: Add disassembly to the '.cfg' output. This is automatically added to the '.cfg' output when using the usual `--dump-cfg` option. Change-Id: I864bfc3a8299c042e72e451cc7730ad8271e4deb --- compiler/Android.mk | 2 +- compiler/optimizing/code_generator.cc | 50 +++++++- compiler/optimizing/code_generator.h | 9 ++ compiler/optimizing/code_generator_arm.cc | 14 +++ compiler/optimizing/code_generator_arm.h | 4 + compiler/optimizing/code_generator_arm64.h | 1 + compiler/optimizing/code_generator_mips64.h | 1 + compiler/optimizing/code_generator_x86.h | 4 + compiler/optimizing/code_generator_x86_64.h | 4 + compiler/optimizing/graph_visualizer.cc | 184 ++++++++++++++++++++++++++-- compiler/optimizing/graph_visualizer.h | 67 ++++++++++ compiler/optimizing/optimizing_compiler.cc | 37 ++++-- compiler/utils/arm64/assembler_arm64.cc | 4 + compiler/utils/arm64/assembler_arm64.h | 3 +- compiler/utils/assembler.h | 1 + disassembler/disassembler.cc | 4 + disassembler/disassembler.h | 7 ++ oatdump/oatdump.cc | 2 +- test/508-checker-disassembly/expected.txt | 0 test/508-checker-disassembly/info.txt | 1 + test/508-checker-disassembly/src/Main.java | 29 +++++ 21 files changed, 405 insertions(+), 23 deletions(-) create mode 100644 test/508-checker-disassembly/expected.txt create mode 100644 test/508-checker-disassembly/info.txt create mode 100644 test/508-checker-disassembly/src/Main.java diff --git a/compiler/Android.mk b/compiler/Android.mk index 60668ed58..dd214067a 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -244,7 +244,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT endif endif - LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime art/disassembler ifeq ($$(art_target_or_host),host) # For compiler driver TLS. diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 64f2c9a98..cd1093580 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -25,6 +25,7 @@ #include "dex/verified_method.h" #include "driver/dex_compilation_unit.h" #include "gc_map_builder.h" +#include "graph_visualizer.h" #include "leb128.h" #include "mapping_table.h" #include "mirror/array-inl.h" @@ -159,12 +160,55 @@ HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const { return block; } +class DisassemblyScope { + public: + DisassemblyScope(HInstruction* instruction, const CodeGenerator& codegen) + : codegen_(codegen), instruction_(instruction), start_offset_(static_cast(-1)) { + if (codegen_.GetDisassemblyInformation() != nullptr) { + start_offset_ = codegen_.GetAssembler().CodeSize(); + } + } + + ~DisassemblyScope() { + // We avoid building this data when we know it will not be used. + if (codegen_.GetDisassemblyInformation() != nullptr) { + codegen_.GetDisassemblyInformation()->AddInstructionInterval( + instruction_, start_offset_, codegen_.GetAssembler().CodeSize()); + } + } + + private: + const CodeGenerator& codegen_; + HInstruction* instruction_; + size_t start_offset_; +}; + + +void CodeGenerator::GenerateSlowPaths() { + size_t code_start = 0; + for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) { + if (disasm_info_ != nullptr) { + code_start = GetAssembler()->CodeSize(); + } + slow_paths_.Get(i)->EmitNativeCode(this); + if (disasm_info_ != nullptr) { + disasm_info_->AddSlowPathInterval(slow_paths_.Get(i), code_start, GetAssembler()->CodeSize()); + } + } +} + void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) { is_baseline_ = is_baseline; HGraphVisitor* instruction_visitor = GetInstructionVisitor(); DCHECK_EQ(current_block_index_, 0u); + + size_t frame_start = GetAssembler()->CodeSize(); GenerateFrameEntry(); DCHECK_EQ(GetAssembler()->cfi().GetCurrentCFAOffset(), static_cast(frame_size_)); + if (disasm_info_ != nullptr) { + disasm_info_->SetFrameEntryInterval(frame_start, GetAssembler()->CodeSize()); + } + for (size_t e = block_order_->Size(); current_block_index_ < e; ++current_block_index_) { HBasicBlock* block = block_order_->Get(current_block_index_); // Don't generate code for an empty block. Its predecessors will branch to its successor @@ -174,6 +218,7 @@ void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) Bind(block); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); + DisassemblyScope disassembly_scope(current, *this); if (is_baseline) { InitLocationsBaseline(current); } @@ -182,10 +227,7 @@ void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) } } - // Generate the slow paths. - for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) { - slow_paths_.Get(i)->EmitNativeCode(this); - } + GenerateSlowPaths(); // Finalize instructions in assember; Finalize(allocator); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index b1f167489..4cecd6136 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -22,6 +22,7 @@ #include "base/bit_field.h" #include "driver/compiler_options.h" #include "globals.h" +#include "graph_visualizer.h" #include "locations.h" #include "memory_region.h" #include "nodes.h" @@ -162,6 +163,7 @@ class CodeGenerator { virtual void Bind(HBasicBlock* block) = 0; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0; virtual Assembler* GetAssembler() = 0; + virtual const Assembler& GetAssembler() const = 0; virtual size_t GetWordSize() const = 0; virtual size_t GetFloatingPointSpillSlotSize() const = 0; virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0; @@ -340,6 +342,9 @@ class CodeGenerator { static void CreateCommonInvokeLocationSummary( HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor); + void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; } + DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; } + protected: CodeGenerator(HGraph* graph, size_t number_of_core_registers, @@ -363,6 +368,7 @@ class CodeGenerator { stack_map_stream_(graph->GetArena()), block_order_(nullptr), is_baseline_(false), + disasm_info_(nullptr), graph_(graph), compiler_options_(compiler_options), slow_paths_(graph->GetArena(), 8), @@ -446,9 +452,12 @@ class CodeGenerator { // Whether we are using baseline. bool is_baseline_; + DisassemblyInformation* disasm_info_; + private: void InitLocationsBaseline(HInstruction* instruction); size_t GetStackOffsetOfSavedRegister(size_t index); + void GenerateSlowPaths(); void CompileInternal(CodeAllocator* allocator, bool is_baseline); void BlockIfInRegister(Location location, bool is_out = false) const; void EmitEnvironment(HEnvironment* environment, SlowPathCode* slow_path); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 71696799f..bd0bfcd30 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -436,6 +436,20 @@ void CodeGeneratorARM::Finalize(CodeAllocator* allocator) { __ AdjustLabelPosition(block_label); } } + // Adjust pc offsets for the disassembly information. + if (disasm_info_ != nullptr) { + GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval(); + frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start); + frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end); + for (auto& it : *disasm_info_->GetInstructionIntervals()) { + it.second.start = __ GetAdjustedPosition(it.second.start); + it.second.end = __ GetAdjustedPosition(it.second.end); + } + for (auto& it : *disasm_info_->GetSlowPathIntervals()) { + it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start); + it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end); + } + } CodeGenerator::Finalize(allocator); } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 1599a2356..5b4b37516 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -254,6 +254,10 @@ class CodeGeneratorARM : public CodeGenerator { return &assembler_; } + const ArmAssembler& GetAssembler() const OVERRIDE { + return assembler_; + } + uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { return GetLabelOf(block)->Position(); } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index f96810ff8..bbe3adc76 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -283,6 +283,7 @@ class CodeGeneratorARM64 : public CodeGenerator { HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; } + const Arm64Assembler& GetAssembler() const OVERRIDE { return assembler_; } vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; } // Emit a write barrier. diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 534154f0d..ec36496a4 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -228,6 +228,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } Mips64Assembler* GetAssembler() OVERRIDE { return &assembler_; } + const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; } void MarkGCCard(GpuRegister object, GpuRegister value); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 696d8d549..1ad89c9bf 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -245,6 +245,10 @@ class CodeGeneratorX86 : public CodeGenerator { return &assembler_; } + const X86Assembler& GetAssembler() const OVERRIDE { + return assembler_; + } + uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { return GetLabelOf(block)->Position(); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 215754cd4..a18e89a3e 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -245,6 +245,10 @@ class CodeGeneratorX86_64 : public CodeGenerator { return &assembler_; } + const X86_64Assembler& GetAssembler() const OVERRIDE { + return assembler_; + } + ParallelMoveResolverX86_64* GetMoveResolver() OVERRIDE { return &move_resolver_; } diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 9fd8d0083..2b85c7c6f 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -16,17 +16,21 @@ #include "graph_visualizer.h" +#include + +#include +#include + #include "code_generator.h" #include "dead_code_elimination.h" +#include "disassembler.h" #include "licm.h" #include "nodes.h" #include "optimization.h" #include "reference_type_propagation.h" #include "register_allocator.h" #include "ssa_liveness_analysis.h" - -#include -#include +#include "utils/assembler.h" namespace art { @@ -87,6 +91,60 @@ std::ostream& operator<<(std::ostream& os, const StringList& list) { } } +typedef Disassembler* create_disasm_prototype(InstructionSet instruction_set, + DisassemblerOptions* options); +class HGraphVisualizerDisassembler { + public: + HGraphVisualizerDisassembler(InstructionSet instruction_set, const uint8_t* base_address) + : instruction_set_(instruction_set) { + libart_disassembler_handle_ = + dlopen(kIsDebugBuild ? "libartd-disassembler.so" : "libart-disassembler.so", RTLD_NOW); + if (libart_disassembler_handle_ == nullptr) { + LOG(WARNING) << "Failed to dlopen libart-disassembler: " << dlerror(); + return; + } + create_disasm_prototype* create_disassembler = reinterpret_cast( + dlsym(libart_disassembler_handle_, "create_disassembler")); + if (create_disassembler == nullptr) { + LOG(WARNING) << "Could not find create_disassembler entry: " << dlerror(); + return; + } + // 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((*create_disassembler)( + instruction_set, + new DisassemblerOptions(/* absolute_addresses */ false, + base_address, + /* can_read_literals */ true))); + } + + ~HGraphVisualizerDisassembler() { + // We need to call ~Disassembler() before we close the library. + disassembler_.reset(); + if (libart_disassembler_handle_ != nullptr) { + dlclose(libart_disassembler_handle_); + } + } + + void Disassemble(std::ostream& output, size_t start, size_t end) const { + const uint8_t* base = disassembler_->GetDisassemblerOptions()->base_address_; + if (instruction_set_ == 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_; + + void* libart_disassembler_handle_; +}; + + /** * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra. */ @@ -96,12 +154,19 @@ class HGraphVisualizerPrinter : public HGraphVisitor { std::ostream& output, const char* pass_name, bool is_after_pass, - const CodeGenerator& codegen) + const CodeGenerator& codegen, + const DisassemblyInformation* disasm_info = nullptr) : HGraphVisitor(graph), output_(output), pass_name_(pass_name), is_after_pass_(is_after_pass), codegen_(codegen), + disasm_info_(disasm_info), + disassembler_(disasm_info_ != nullptr + ? new HGraphVisualizerDisassembler( + codegen_.GetInstructionSet(), + codegen_.GetAssembler().CodeBufferBaseAddress()) + : nullptr), indent_(0) {} void StartTag(const char* name) { @@ -173,6 +238,9 @@ class HGraphVisualizerPrinter : public HGraphVisitor { HBasicBlock* predecessor = block->GetPredecessors().Get(i); output_ << " \"B" << predecessor->GetBlockId() << "\" "; } + if (block->IsEntryBlock() && (disasm_info_ != nullptr)) { + output_ << " \"" << kDisassemblyBlockFrameEntry << "\" "; + } output_<< std::endl; } @@ -183,6 +251,11 @@ class HGraphVisualizerPrinter : public HGraphVisitor { HBasicBlock* successor = block->GetSuccessors().Get(i); output_ << " \"B" << successor->GetBlockId() << "\" "; } + if (block->IsExitBlock() && + (disasm_info_ != nullptr) && + !disasm_info_->GetSlowPathIntervals().empty()) { + output_ << " \"" << kDisassemblyBlockSlowPaths << "\" "; + } output_<< std::endl; } @@ -266,9 +339,9 @@ class HGraphVisualizerPrinter : public HGraphVisitor { StartAttributeStream("kind") << barrier->GetBarrierKind(); } - void VisitLoadClass(HLoadClass* load_cass) OVERRIDE { + void VisitLoadClass(HLoadClass* load_class) OVERRIDE { StartAttributeStream("gen_clinit_check") << std::boolalpha - << load_cass->MustGenerateClinitCheck() << std::noboolalpha; + << load_class->MustGenerateClinitCheck() << std::noboolalpha; } void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { @@ -378,10 +451,20 @@ class HGraphVisualizerPrinter : public HGraphVisitor { } } } + 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_ << std::endl; + disassembler_->Disassemble(output_, it->second.start, it->second.end); + } + } } void PrintInstructions(const HInstructionList& list) { - const char* kEndInstructionMarker = "<|@"; for (HInstructionIterator it(list); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); int bci = 0; @@ -399,11 +482,83 @@ class HGraphVisualizerPrinter : public HGraphVisitor { } } + 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_ << std::endl; + disassembler_->Disassemble(output_, frame_entry.start, frame_entry.end); + } + output_ << kEndInstructionMarker << std::endl; + 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() << std::endl; + disassembler_->Disassemble(output_, info.code_interval.start, info.code_interval.end); + output_ << kEndInstructionMarker << std::endl; + } + DumpEndOfDisassemblyBlock(); + } + void Run() { StartTag("cfg"); std::string pass_desc = std::string(pass_name_) + (is_after_pass_ ? " (after)" : " (before)"); PrintProperty("name", pass_desc.c_str()); + if (disasm_info_ != nullptr) { + DumpDisassemblyBlockForFrameEntry(); + } VisitInsertionOrder(); + if (disasm_info_ != nullptr) { + DumpDisassemblyBlockForSlowPaths(); + } EndTag("cfg"); } @@ -450,11 +605,17 @@ class HGraphVisualizerPrinter : public HGraphVisitor { EndTag("block"); } + static constexpr const char* const kEndInstructionMarker = "<|@"; + static constexpr const char* const kDisassemblyBlockFrameEntry = "FrameEntry"; + static constexpr const char* const kDisassemblyBlockSlowPaths = "SlowPaths"; + private: std::ostream& output_; const char* pass_name_; const bool is_after_pass_; const CodeGenerator& codegen_; + const DisassemblyInformation* disasm_info_; + std::unique_ptr disassembler_; size_t indent_; DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter); @@ -483,4 +644,13 @@ void HGraphVisualizer::DumpGraph(const char* pass_name, bool is_after_pass) cons } } +void HGraphVisualizer::DumpGraphWithDisassembly() const { + DCHECK(output_ != nullptr); + if (!graph_->GetBlocks().IsEmpty()) { + HGraphVisualizerPrinter printer( + graph_, *output_, "disassembly", true, codegen_, codegen_.GetDisassemblyInformation()); + printer.Run(); + } +} + } // namespace art diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h index 513bceb36..b6b66df60 100644 --- a/compiler/optimizing/graph_visualizer.h +++ b/compiler/optimizing/graph_visualizer.h @@ -19,6 +19,8 @@ #include +#include "arch/instruction_set.h" +#include "base/arena_containers.h" #include "base/value_object.h" namespace art { @@ -26,11 +28,75 @@ namespace art { class CodeGenerator; class DexCompilationUnit; class HGraph; +class HInstruction; +class SlowPathCode; /** * This class outputs the HGraph in the C1visualizer format. * Note: Currently only works if the compiler is single threaded. */ +struct GeneratedCodeInterval { + size_t start; + size_t end; +}; + +struct SlowPathCodeInfo { + const SlowPathCode* slow_path; + GeneratedCodeInterval code_interval; +}; + +// This information is filled by the code generator. It will be used by the +// graph visualizer to associate disassembly of the generated code with the +// instructions and slow paths. We assume that the generated code follows the +// following structure: +// - frame entry +// - instructions +// - slow paths +class DisassemblyInformation { + public: + explicit DisassemblyInformation(ArenaAllocator* allocator) + : frame_entry_interval_({0, 0}), + instruction_intervals_(std::less(), allocator->Adapter()), + slow_path_intervals_(allocator->Adapter()) {} + + void SetFrameEntryInterval(size_t start, size_t end) { + frame_entry_interval_ = {start, end}; + } + + void AddInstructionInterval(HInstruction* instr, size_t start, size_t end) { + instruction_intervals_.Put(instr, {start, end}); + } + + void AddSlowPathInterval(SlowPathCode* slow_path, size_t start, size_t end) { + slow_path_intervals_.push_back({slow_path, {start, end}}); + } + + GeneratedCodeInterval GetFrameEntryInterval() const { + return frame_entry_interval_; + } + + GeneratedCodeInterval* GetFrameEntryInterval() { + return &frame_entry_interval_; + } + + const ArenaSafeMap& GetInstructionIntervals() const { + return instruction_intervals_; + } + + ArenaSafeMap* GetInstructionIntervals() { + return &instruction_intervals_; + } + + const ArenaVector& GetSlowPathIntervals() const { return slow_path_intervals_; } + + ArenaVector* GetSlowPathIntervals() { return &slow_path_intervals_; } + + private: + GeneratedCodeInterval frame_entry_interval_; + ArenaSafeMap instruction_intervals_; + ArenaVector slow_path_intervals_; +}; + class HGraphVisualizer : public ValueObject { public: HGraphVisualizer(std::ostream* output, @@ -39,6 +105,7 @@ class HGraphVisualizer : public ValueObject { void PrintHeader(const char* method_name) const; void DumpGraph(const char* pass_name, bool is_after_pass = true) const; + void DumpGraphWithDisassembly() const; private: std::ostream* const output_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index ad6781333..0c7b6f709 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -92,19 +92,21 @@ class PassInfoPrinter : public ValueObject { public: PassInfoPrinter(HGraph* graph, const char* method_name, - const CodeGenerator& codegen, + CodeGenerator* codegen, std::ostream* visualizer_output, CompilerDriver* compiler_driver) : method_name_(method_name), timing_logger_enabled_(compiler_driver->GetDumpPasses()), timing_logger_(method_name, true, true), + disasm_info_(graph->GetArena()), visualizer_enabled_(!compiler_driver->GetDumpCfgFileName().empty()), - visualizer_(visualizer_output, graph, codegen) { + visualizer_(visualizer_output, graph, *codegen) { if (strstr(method_name, kStringFilter) == nullptr) { timing_logger_enabled_ = visualizer_enabled_ = false; } if (visualizer_enabled_) { visualizer_.PrintHeader(method_name_); + codegen->SetDisassemblyInformation(&disasm_info_); } } @@ -115,6 +117,12 @@ class PassInfoPrinter : public ValueObject { } } + void DumpDisassembly() const { + if (visualizer_enabled_) { + visualizer_.DumpGraphWithDisassembly(); + } + } + private: void StartPass(const char* pass_name) { // Dump graph first, then start timer. @@ -141,6 +149,8 @@ class PassInfoPrinter : public ValueObject { bool timing_logger_enabled_; TimingLogger timing_logger_; + DisassemblyInformation disasm_info_; + bool visualizer_enabled_; HGraphVisualizer visualizer_; @@ -224,12 +234,13 @@ class OptimizingCompiler FINAL : public Compiler { CodeGenerator* codegen, CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, - PassInfoPrinter* pass_info) const; + PassInfoPrinter* pass_info_printer) const; // Just compile without doing optimizations. CompiledMethod* CompileBaseline(CodeGenerator* codegen, CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit) const; + const DexCompilationUnit& dex_compilation_unit, + PassInfoPrinter* pass_info_printer) const; std::unique_ptr compilation_stats_; @@ -429,7 +440,7 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, MaybeRecordStat(MethodCompilationStat::kCompiledOptimized); - return CompiledMethod::SwapAllocCompiledMethod( + CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( compiler_driver, codegen->GetInstructionSet(), ArrayRef(allocator.GetMemory()), @@ -445,12 +456,15 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, ArrayRef(), // native_gc_map. ArrayRef(*codegen->GetAssembler()->cfi().data()), ArrayRef()); + pass_info_printer->DumpDisassembly(); + return compiled_method; } CompiledMethod* OptimizingCompiler::CompileBaseline( CodeGenerator* codegen, CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) const { + const DexCompilationUnit& dex_compilation_unit, + PassInfoPrinter* pass_info_printer) const { CodeVectorAllocator allocator; codegen->CompileBaseline(&allocator); @@ -466,7 +480,7 @@ CompiledMethod* OptimizingCompiler::CompileBaseline( codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit); MaybeRecordStat(MethodCompilationStat::kCompiledBaseline); - return CompiledMethod::SwapAllocCompiledMethod( + CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( compiler_driver, codegen->GetInstructionSet(), ArrayRef(allocator.GetMemory()), @@ -482,6 +496,8 @@ CompiledMethod* OptimizingCompiler::CompileBaseline( AlignVectorSize(gc_map), ArrayRef(*codegen->GetAssembler()->cfi().data()), ArrayRef()); + pass_info_printer->DumpDisassembly(); + return compiled_method; } CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item, @@ -557,7 +573,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite PassInfoPrinter pass_info_printer(graph, method_name.c_str(), - *codegen.get(), + codegen.get(), visualizer_output_.get(), compiler_driver); @@ -617,7 +633,10 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite MaybeRecordStat(MethodCompilationStat::kNotOptimizedRegisterAllocator); } - return CompileBaseline(codegen.get(), compiler_driver, dex_compilation_unit); + return CompileBaseline(codegen.get(), + compiler_driver, + dex_compilation_unit, + &pass_info_printer); } else { return nullptr; } diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index eb8de0620..077579c88 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -44,6 +44,10 @@ size_t Arm64Assembler::CodeSize() const { return vixl_masm_->BufferCapacity() - vixl_masm_->RemainingBufferSpace(); } +const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const { + return vixl_masm_->GetStartAddress(); +} + void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { // Copy the instructions from the buffer. MemoryRegion from(vixl_masm_->GetStartAddress(), CodeSize()); diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index b53c11bc2..db95537f9 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -77,7 +77,8 @@ class Arm64Assembler FINAL : public Assembler { void FinalizeCode() OVERRIDE; // Size of generated code. - size_t CodeSize() const; + size_t CodeSize() const OVERRIDE; + const uint8_t* CodeBufferBaseAddress() const OVERRIDE; // Copy instructions out of assembly buffer into the given region of memory. void FinalizeInstructions(const MemoryRegion& region); diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 0381af395..ee2d594e6 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -386,6 +386,7 @@ class Assembler { // Size of generated code virtual size_t CodeSize() const { return buffer_.Size(); } + virtual const uint8_t* CodeBufferBaseAddress() const { return buffer_.contents(); } // Copy instructions out of assembly buffer into the given region of memory virtual void FinalizeInstructions(const MemoryRegion& region) { diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc index 6334717fe..e604c1f62 100644 --- a/disassembler/disassembler.cc +++ b/disassembler/disassembler.cc @@ -55,4 +55,8 @@ std::string Disassembler::FormatInstructionPointer(const uint8_t* begin) { } } +Disassembler* create_disassembler(InstructionSet instruction_set, DisassemblerOptions* options) { + return Disassembler::Create(instruction_set, options); +} + } // namespace art diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h index 966ee3ac8..b99e5c2df 100644 --- a/disassembler/disassembler.h +++ b/disassembler/disassembler.h @@ -63,6 +63,10 @@ class Disassembler { // Dump instructions within a range. virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) = 0; + const DisassemblerOptions* GetDisassemblerOptions() const { + return disassembler_options_; + } + protected: explicit Disassembler(DisassemblerOptions* disassembler_options) : disassembler_options_(disassembler_options) { @@ -80,6 +84,9 @@ static inline bool HasBitSet(uint32_t value, uint32_t bit) { return (value & (1 << bit)) != 0; } +extern "C" +Disassembler* create_disassembler(InstructionSet instruction_set, DisassemblerOptions* options); + } // namespace art #endif // ART_DISASSEMBLER_DISASSEMBLER_H_ diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 9e9dea64c..b3801b351 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -355,7 +355,7 @@ class OatDumper { disassembler_(Disassembler::Create(instruction_set_, new DisassemblerOptions(options_.absolute_addresses_, oat_file.Begin(), - true /* can_read_litals_ */))) { + true /* can_read_literals_ */))) { CHECK(options_.class_loader_ != nullptr); CHECK(options_.class_filter_ != nullptr); CHECK(options_.method_filter_ != nullptr); diff --git a/test/508-checker-disassembly/expected.txt b/test/508-checker-disassembly/expected.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/508-checker-disassembly/info.txt b/test/508-checker-disassembly/info.txt new file mode 100644 index 000000000..4a25b21d3 --- /dev/null +++ b/test/508-checker-disassembly/info.txt @@ -0,0 +1 @@ +Check that inlining disassembly in the .cfg works correctly. diff --git a/test/508-checker-disassembly/src/Main.java b/test/508-checker-disassembly/src/Main.java new file mode 100644 index 000000000..29c9374ae --- /dev/null +++ b/test/508-checker-disassembly/src/Main.java @@ -0,0 +1,29 @@ +/* +* Copyright (C) 2015 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. +*/ + +public class Main { + // A very simple check that disassembly information has been added to the + // graph. We check that sections have been added for the frame entry and a + // slow path. + /// CHECK-START: int Main.DisassembledFunction(int) disassembly (after) + /// CHECK: FrameEntry + /// CHECK: DivZeroCheckSlowPath{{.*}} + public int DisassembledFunction(int arg) { + return 7 / arg; + } + + public static void main(String[] args) {} +} -- 2.11.0