diff options
Diffstat (limited to 'compiler/optimizing')
31 files changed, 4204 insertions, 116 deletions
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index 97b3725da1..900dabea0e 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -1759,7 +1759,9 @@ class BCEVisitor : public HGraphVisitor { ValueBound lower_bound = range->GetLower(); DCHECK(lower_bound.IsConstant()); DCHECK(const_instr->GetValue() <= kMaxConstantForAddingDeoptimize); - DCHECK_EQ(lower_bound.GetConstant(), const_instr->GetValue() + 1); + // Note that the lower bound of the array length may have been refined + // through other instructions (such as `HNewArray(length - 4)`). + DCHECK_LE(const_instr->GetValue() + 1, lower_bound.GetConstant()); // If array_length is less than lower_const, deoptimize. HBoundsCheck* bounds_check = first_constant_index_bounds_check_map_.Get( diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 793dd28157..c49752642b 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -483,10 +483,11 @@ void HGraphBuilder::Binop_23x_shift(const Instruction& instruction, void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction, Primitive::Type type, - HCompare::Bias bias) { + HCompare::Bias bias, + uint32_t dex_pc) { HInstruction* first = LoadLocal(instruction.VRegB(), type); HInstruction* second = LoadLocal(instruction.VRegC(), type); - current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias)); + current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc)); UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); } @@ -661,7 +662,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex()) && (target_method.dex_file == outer_compilation_unit_->GetDexFile()); - if (optimized_invoke_type == kStatic) { + if (optimized_invoke_type == kStatic && !is_string_init) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<4> hs(soa.Self()); Handle<mirror::DexCache> dex_cache(hs.NewHandle( @@ -680,26 +681,30 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); - Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass())); + Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); // The index at which the method's class is stored in the DexCache's type array. uint32_t storage_index = DexFile::kDexNoIndex; - bool is_referrer_class = (resolved_method->GetDeclaringClass() == referrer_class.Get()); - if (is_referrer_class) { - storage_index = referrer_class->GetDexTypeIndex(); + bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get()); + if (is_outer_class) { + storage_index = outer_class->GetDexTypeIndex(); } else if (outer_dex_cache.Get() == dex_cache.Get()) { // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer. compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(), - referrer_class.Get(), + GetCompilingClass(), resolved_method, method_idx, &storage_index); } - if (referrer_class.Get()->IsSubClass(resolved_method->GetDeclaringClass())) { - // If the referrer class is the declaring class or a subclass + if (!outer_class->IsInterface() + && outer_class->IsSubClass(resolved_method->GetDeclaringClass())) { + // If the outer class is the declaring class or a subclass // of the declaring class, no class initialization is needed // before the static method call. + // Note that in case of inlining, we do not need to add clinit checks + // to calls that satisfy this subclass check with any inlined methods. This + // will be detected by the optimization passes. clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; } else if (storage_index != DexFile::kDexNoIndex) { // If the method's class type index is available, check @@ -720,7 +725,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, graph_->GetCurrentMethod(), storage_index, *dex_compilation_unit_->GetDexFile(), - is_referrer_class, + is_outer_class, dex_pc); current_block_->AddInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); @@ -809,6 +814,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, // Add move-result for StringFactory method. if (is_string_init) { uint32_t orig_this_reg = is_range ? register_index : args[0]; + UpdateLocal(orig_this_reg, invoke); const VerifiedMethod* verified_method = compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex()); if (verified_method == nullptr) { @@ -822,10 +828,10 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, if (map_it != string_init_map.end()) { std::set<uint32_t> reg_set = map_it->second; for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { - UpdateLocal(*set_it, invoke); + HInstruction* load_local = LoadLocal(orig_this_reg, Primitive::kPrimNot); + UpdateLocal(*set_it, load_local); } } - UpdateLocal(orig_this_reg, invoke); } return true; } @@ -878,17 +884,25 @@ bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction, return true; } -mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const { +static mirror::Class* GetClassFrom(CompilerDriver* driver, + const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<2> hs(soa.Self()); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + const DexFile& dex_file = *compilation_unit.GetDexFile(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); + soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader()))); + Handle<mirror::DexCache> dex_cache(hs.NewHandle( + compilation_unit.GetClassLinker()->FindDexCache(dex_file))); + + return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); +} + +mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const { + return GetClassFrom(compiler_driver_, *outer_compilation_unit_); +} - return compiler_driver_->ResolveCompilingMethodsClass( - soa, outer_dex_cache, class_loader, outer_compilation_unit_); +mirror::Class* HGraphBuilder::GetCompilingClass() const { + return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const { @@ -900,9 +914,9 @@ bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const { soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( soa, dex_cache, class_loader, type_index, dex_compilation_unit_))); - Handle<mirror::Class> compiling_class(hs.NewHandle(GetOutermostCompilingClass())); + Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); - return compiling_class.Get() == cls.Get(); + return outer_class.Get() == cls.Get(); } bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, @@ -928,20 +942,20 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); - Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass())); + Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); // The index at which the field's class is stored in the DexCache's type array. uint32_t storage_index; - bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass()); - if (is_referrer_class) { - storage_index = referrer_class->GetDexTypeIndex(); + bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass()); + if (is_outer_class) { + storage_index = outer_class->GetDexTypeIndex(); } else if (outer_dex_cache.Get() != dex_cache.Get()) { // The compiler driver cannot currently understand multiple dex caches involved. Just bailout. return false; } else { std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField( outer_dex_cache.Get(), - referrer_class.Get(), + GetCompilingClass(), resolved_field, field_index, &storage_index); @@ -959,12 +973,12 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(), storage_index, *dex_compilation_unit_->GetDexFile(), - is_referrer_class, + is_outer_class, dex_pc); current_block_->AddInstruction(constant); HInstruction* cls = constant; - if (!is_initialized && !is_referrer_class) { + if (!is_initialized && !is_outer_class) { cls = new (arena_) HClinitCheck(constant, dex_pc); current_block_->AddInstruction(cls); } @@ -1463,21 +1477,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::RETURN: { - DCHECK_NE(return_type_, Primitive::kPrimNot); - DCHECK_NE(return_type_, Primitive::kPrimLong); - DCHECK_NE(return_type_, Primitive::kPrimDouble); BuildReturn(instruction, return_type_); break; } case Instruction::RETURN_OBJECT: { - DCHECK(return_type_ == Primitive::kPrimNot); BuildReturn(instruction, return_type_); break; } case Instruction::RETURN_WIDE: { - DCHECK(return_type_ == Primitive::kPrimDouble || return_type_ == Primitive::kPrimLong); BuildReturn(instruction, return_type_); break; } @@ -2105,27 +2114,27 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; case Instruction::CMP_LONG: { - Binop_23x_cmp(instruction, Primitive::kPrimLong, HCompare::kNoBias); + Binop_23x_cmp(instruction, Primitive::kPrimLong, HCompare::kNoBias, dex_pc); break; } case Instruction::CMPG_FLOAT: { - Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kGtBias); + Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kGtBias, dex_pc); break; } case Instruction::CMPG_DOUBLE: { - Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kGtBias); + Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kGtBias, dex_pc); break; } case Instruction::CMPL_FLOAT: { - Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kLtBias); + Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kLtBias, dex_pc); break; } case Instruction::CMPL_DOUBLE: { - Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kLtBias); + Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kLtBias, dex_pc); break; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 36503ce43a..052aaf8b42 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -117,7 +117,10 @@ class HGraphBuilder : public ValueObject { template<typename T> void Binop_23x_shift(const Instruction& instruction, Primitive::Type type); - void Binop_23x_cmp(const Instruction& instruction, Primitive::Type type, HCompare::Bias bias); + void Binop_23x_cmp(const Instruction& instruction, + Primitive::Type type, + HCompare::Bias bias, + uint32_t dex_pc); template<typename T> void Binop_12x(const Instruction& instruction, Primitive::Type type); @@ -222,8 +225,12 @@ class HGraphBuilder : public ValueObject { void MaybeRecordStat(MethodCompilationStat compilation_stat); + // Returns the outer-most compiling method's class. mirror::Class* GetOutermostCompilingClass() const; + // Returns the class whose method is being compiled. + mirror::Class* GetCompilingClass() const; + // Returns whether `type_index` points to the outer-most compiling method's class. bool IsOutermostCompilingClass(uint16_t type_index) const; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 09f7d86605..cd10935806 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -20,10 +20,12 @@ #include "code_generator_arm64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" +#include "code_generator_mips64.h" #include "compiled_method.h" #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" @@ -158,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<size_t>(-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<int>(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 @@ -173,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); } @@ -181,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); @@ -486,6 +529,11 @@ CodeGenerator* CodeGenerator::Create(HGraph* graph, } case kMips: return nullptr; + case kMips64: { + return new mips64::CodeGeneratorMIPS64(graph, + *isa_features.AsMips64InstructionSetFeatures(), + compiler_options); + } case kX86: { return new x86::CodeGeneratorX86(graph, *isa_features.AsX86InstructionSetFeatures(), @@ -651,18 +699,18 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path) { if (instruction != nullptr) { - // The code generated for some type conversions may call the - // runtime, thus normally requiring a subsequent call to this - // method. However, the method verifier does not produce PC - // information for certain instructions, which are considered "atomic" - // (they cannot join a GC). + // The code generated for some type conversions and comparisons + // may call the runtime, thus normally requiring a subsequent + // call to this method. However, the method verifier does not + // produce PC information for certain instructions, which are + // considered "atomic" (they cannot join a GC). // Therefore we do not currently record PC information for such // instructions. As this may change later, we added this special // case so that code generators may nevertheless call // CodeGenerator::RecordPcInfo without triggering an error in // CodeGenerator::BuildNativeGCMap ("Missing ref for dex pc 0x") // thereafter. - if (instruction->IsTypeConversion()) { + if (instruction->IsTypeConversion() || instruction->IsCompare()) { return; } if (instruction->IsRem()) { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 5b0abd76b3..4cecd61365 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" @@ -97,6 +98,8 @@ class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> { return saved_fpu_stack_offsets_[reg]; } + virtual const char* GetDescription() const = 0; + protected: static constexpr size_t kMaximumNumberOfExpectedRegisters = 32; static constexpr uint32_t kRegisterNotSaved = -1; @@ -160,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; @@ -338,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, @@ -361,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), @@ -444,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 bb5debba29..9abe2e7be4 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -69,6 +69,8 @@ class NullCheckSlowPathARM : public SlowPathCodeARM { QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); } + const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } + private: HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); @@ -85,6 +87,8 @@ class DivZeroCheckSlowPathARM : public SlowPathCodeARM { QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); } + const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } + private: HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); @@ -118,6 +122,8 @@ class SuspendCheckSlowPathARM : public SlowPathCodeARM { return successor_; } + const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } + private: HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. @@ -155,6 +161,8 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); } + const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } + private: HBoundsCheck* const instruction_; const Location index_location_; @@ -197,6 +205,8 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { __ b(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; } + private: // The class this slow path will load. HLoadClass* const cls_; @@ -236,6 +246,8 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { __ b(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } + private: HLoadString* const instruction_; @@ -286,6 +298,8 @@ class TypeCheckSlowPathARM : public SlowPathCodeARM { __ b(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; } + private: HInstruction* const instruction_; const Location class_to_check_; @@ -310,6 +324,8 @@ class DeoptimizationSlowPathARM : public SlowPathCodeARM { arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); } + const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } + private: HInstruction* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); @@ -420,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); } @@ -2586,7 +2616,9 @@ void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { case Primitive::kPrimInt: { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(op->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + // Make the output overlap, as it will be used to hold the masked + // second input. + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); break; } case Primitive::kPrimLong: { @@ -2617,13 +2649,13 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { // Arm doesn't mask the shift count so we need to do it ourselves. if (second.IsRegister()) { Register second_reg = second.AsRegister<Register>(); - __ and_(second_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); + __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); if (op->IsShl()) { - __ Lsl(out_reg, first_reg, second_reg); + __ Lsl(out_reg, first_reg, out_reg); } else if (op->IsShr()) { - __ Asr(out_reg, first_reg, second_reg); + __ Asr(out_reg, first_reg, out_reg); } else { - __ Lsr(out_reg, first_reg, second_reg); + __ Lsr(out_reg, first_reg, out_reg); } } else { int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); @@ -2652,44 +2684,44 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { Register second_reg = second.AsRegister<Register>(); if (op->IsShl()) { + __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); // Shift the high part - __ and_(second_reg, second_reg, ShifterOperand(63)); - __ Lsl(o_h, high, second_reg); + __ Lsl(o_h, high, o_l); // Shift the low part and `or` what overflew on the high part - __ rsb(temp, second_reg, ShifterOperand(32)); + __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); __ Lsr(temp, low, temp); __ orr(o_h, o_h, ShifterOperand(temp)); // If the shift is > 32 bits, override the high part - __ subs(temp, second_reg, ShifterOperand(32)); + __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); __ it(PL); __ Lsl(o_h, low, temp, false, PL); // Shift the low part - __ Lsl(o_l, low, second_reg); + __ Lsl(o_l, low, o_l); } else if (op->IsShr()) { + __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); // Shift the low part - __ and_(second_reg, second_reg, ShifterOperand(63)); - __ Lsr(o_l, low, second_reg); + __ Lsr(o_l, low, o_h); // Shift the high part and `or` what underflew on the low part - __ rsb(temp, second_reg, ShifterOperand(32)); + __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); __ Lsl(temp, high, temp); __ orr(o_l, o_l, ShifterOperand(temp)); // If the shift is > 32 bits, override the low part - __ subs(temp, second_reg, ShifterOperand(32)); + __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); __ it(PL); __ Asr(o_l, high, temp, false, PL); // Shift the high part - __ Asr(o_h, high, second_reg); + __ Asr(o_h, high, o_h); } else { + __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); // same as Shr except we use `Lsr`s and not `Asr`s - __ and_(second_reg, second_reg, ShifterOperand(63)); - __ Lsr(o_l, low, second_reg); - __ rsb(temp, second_reg, ShifterOperand(32)); + __ Lsr(o_l, low, o_h); + __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); __ Lsl(temp, high, temp); __ orr(o_l, o_l, ShifterOperand(temp)); - __ subs(temp, second_reg, ShifterOperand(32)); + __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); __ it(PL); __ Lsr(o_l, high, temp, false, PL); - __ Lsr(o_h, high, second_reg); + __ Lsr(o_h, high, o_h); } break; } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 1599a23568..953e733c44 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -32,6 +32,7 @@ class SlowPathCodeARM; // Use a local definition to prevent copying mistakes. static constexpr size_t kArmWordSize = kArmPointerSize; +static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte; static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 }; static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); @@ -254,6 +255,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.cc b/compiler/optimizing/code_generator_arm64.cc index 3c8f117011..7ec6b54285 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -213,6 +213,8 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } + const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; } + private: HBoundsCheck* const instruction_; const Location index_location_; @@ -233,6 +235,8 @@ class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); } + const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; } + private: HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64); @@ -278,6 +282,8 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { __ B(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM64"; } + private: // The class this slow path will load. HLoadClass* const cls_; @@ -319,6 +325,8 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { __ B(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; } + private: HLoadString* const instruction_; @@ -337,6 +345,8 @@ class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); } + const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; } + private: HNullCheck* const instruction_; @@ -373,6 +383,8 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { return successor_; } + const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; } + private: HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. @@ -429,6 +441,8 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { __ B(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM64"; } + private: HInstruction* const instruction_; const Location class_to_check_; @@ -453,6 +467,8 @@ class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 { arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); } + const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; } + private: HInstruction* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index f96810ff80..bbe3adc76b 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.cc b/compiler/optimizing/code_generator_mips64.cc new file mode 100644 index 0000000000..ab684d41d4 --- /dev/null +++ b/compiler/optimizing/code_generator_mips64.cc @@ -0,0 +1,3282 @@ +/* + * 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. + */ + +#include "code_generator_mips64.h" + +#include "entrypoints/quick/quick_entrypoints.h" +#include "entrypoints/quick/quick_entrypoints_enum.h" +#include "gc/accounting/card_table.h" +#include "intrinsics.h" +#include "art_method.h" +#include "mirror/array-inl.h" +#include "mirror/class-inl.h" +#include "offsets.h" +#include "thread.h" +#include "utils/mips64/assembler_mips64.h" +#include "utils/assembler.h" +#include "utils/stack_checks.h" + +namespace art { +namespace mips64 { + +static constexpr int kCurrentMethodStackOffset = 0; +static constexpr GpuRegister kMethodRegisterArgument = A0; + +// We need extra temporary/scratch registers (in addition to AT) in some cases. +static constexpr GpuRegister TMP = T8; +static constexpr FpuRegister FTMP = F8; + +// ART Thread Register. +static constexpr GpuRegister TR = S1; + +Location Mips64ReturnLocation(Primitive::Type return_type) { + switch (return_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + case Primitive::kPrimLong: + return Location::RegisterLocation(V0); + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + return Location::FpuRegisterLocation(F0); + + case Primitive::kPrimVoid: + return Location(); + } + UNREACHABLE(); +} + +Location InvokeDexCallingConventionVisitorMIPS64::GetReturnLocation(Primitive::Type type) const { + return Mips64ReturnLocation(type); +} + +Location InvokeDexCallingConventionVisitorMIPS64::GetMethodLocation() const { + return Location::RegisterLocation(kMethodRegisterArgument); +} + +Location InvokeDexCallingConventionVisitorMIPS64::GetNextLocation(Primitive::Type type) { + Location next_location; + if (type == Primitive::kPrimVoid) { + LOG(FATAL) << "Unexpected parameter type " << type; + } + + if (Primitive::IsFloatingPointType(type) && + (float_index_ < calling_convention.GetNumberOfFpuRegisters())) { + next_location = Location::FpuRegisterLocation( + calling_convention.GetFpuRegisterAt(float_index_++)); + gp_index_++; + } else if (!Primitive::IsFloatingPointType(type) && + (gp_index_ < calling_convention.GetNumberOfRegisters())) { + next_location = Location::RegisterLocation(calling_convention.GetRegisterAt(gp_index_++)); + float_index_++; + } else { + size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_); + next_location = Primitive::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset) + : Location::StackSlot(stack_offset); + } + + // Space on the stack is reserved for all arguments. + stack_index_ += Primitive::Is64BitType(type) ? 2 : 1; + + // TODO: review + + // TODO: shouldn't we use a whole machine word per argument on the stack? + // Implicit 4-byte method pointer (and such) will cause misalignment. + + return next_location; +} + +Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type) { + return Mips64ReturnLocation(type); +} + +#define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()-> +#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, x).Int32Value() + +class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction, + Location index_location, + Location length_location) + : instruction_(instruction), + index_location_(index_location), + length_location_(length_location) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + __ Bind(GetEntryLabel()); + // We're moving two locations to locations that could overlap, so we need a parallel + // move resolver. + InvokeRuntimeCallingConvention calling_convention; + codegen->EmitParallelMoves(index_location_, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Primitive::kPrimInt, + length_location_, + Location::RegisterLocation(calling_convention.GetRegisterAt(1)), + Primitive::kPrimInt); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); + } + + const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS64"; } + + private: + HBoundsCheck* const instruction_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS64); +}; + +class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : instruction_(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + __ Bind(GetEntryLabel()); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); + } + + const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS64"; } + + private: + HDivZeroCheck* const instruction_; + DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS64); +}; + +class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + LoadClassSlowPathMIPS64(HLoadClass* cls, + HInstruction* at, + uint32_t dex_pc, + bool do_clinit) + : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + DCHECK(at->IsLoadClass() || at->IsClinitCheck()); + } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = at_->GetLocations(); + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + InvokeRuntimeCallingConvention calling_convention; + __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); + int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage) + : QUICK_ENTRY_POINT(pInitializeType); + mips64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); + if (do_clinit_) { + CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); + } else { + CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); + } + + // Move the class to the desired location. + Location out = locations->Out(); + if (out.IsValid()) { + DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); + Primitive::Type type = at_->GetType(); + mips64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); + } + + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathMIPS64"; } + + private: + // The class this slow path will load. + HLoadClass* const cls_; + + // The instruction where this slow path is happening. + // (Might be the load class or an initialization check). + HInstruction* const at_; + + // The dex PC of `at_`. + const uint32_t dex_pc_; + + // Whether to initialize the class. + const bool do_clinit_; + + DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS64); +}; + +class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : instruction_(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + InvokeRuntimeCallingConvention calling_convention; + __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); + Primitive::Type type = instruction_->GetType(); + mips64_codegen->MoveLocation(locations->Out(), + calling_convention.GetReturnLocation(type), + type); + + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; } + + private: + HLoadString* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64); +}; + +class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : instruction_(instr) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + __ Bind(GetEntryLabel()); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); + } + + const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS64"; } + + private: + HNullCheck* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS64); +}; + +class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + explicit SuspendCheckSlowPathMIPS64(HSuspendCheck* instruction, + HBasicBlock* successor) + : instruction_(instruction), successor_(successor) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, instruction_->GetLocations()); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pTestSuspend), + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickTestSuspend, void, void>(); + RestoreLiveRegisters(codegen, instruction_->GetLocations()); + if (successor_ == nullptr) { + __ B(GetReturnLabel()); + } else { + __ B(mips64_codegen->GetLabelOf(successor_)); + } + } + + Label* GetReturnLabel() { + DCHECK(successor_ == nullptr); + return &return_label_; + } + + const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS64"; } + + private: + HSuspendCheck* const instruction_; + // If not null, the block to branch to after the suspend check. + HBasicBlock* const successor_; + + // If `successor_` is null, the label to branch to after the suspend check. + Label return_label_; + + DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathMIPS64); +}; + +class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + TypeCheckSlowPathMIPS64(HInstruction* instruction, + Location class_to_check, + Location object_class, + uint32_t dex_pc) + : instruction_(instruction), + class_to_check_(class_to_check), + object_class_(object_class), + dex_pc_(dex_pc) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + DCHECK(instruction_->IsCheckCast() + || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + // We're moving two locations to locations that could overlap, so we need a parallel + // move resolver. + InvokeRuntimeCallingConvention calling_convention; + codegen->EmitParallelMoves(class_to_check_, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Primitive::kPrimNot, + object_class_, + Location::RegisterLocation(calling_convention.GetRegisterAt(1)), + Primitive::kPrimNot); + + if (instruction_->IsInstanceOf()) { + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), + instruction_, + dex_pc_, + this); + Primitive::Type ret_type = instruction_->GetType(); + Location ret_loc = calling_convention.GetReturnLocation(ret_type); + mips64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type); + CheckEntrypointTypes<kQuickInstanceofNonTrivial, + uint32_t, + const mirror::Class*, + const mirror::Class*>(); + } else { + DCHECK(instruction_->IsCheckCast()); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), instruction_, dex_pc_, this); + CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>(); + } + + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS64"; } + + private: + HInstruction* const instruction_; + const Location class_to_check_; + const Location object_class_; + uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS64); +}; + +class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 { + public: + explicit DeoptimizationSlowPathMIPS64(HInstruction* instruction) + : instruction_(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, instruction_->GetLocations()); + DCHECK(instruction_->IsDeoptimize()); + HDeoptimize* deoptimize = instruction_->AsDeoptimize(); + uint32_t dex_pc = deoptimize->GetDexPc(); + CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); + mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); + } + + const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS64"; } + + private: + HInstruction* const instruction_; + DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS64); +}; + +CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, + const Mips64InstructionSetFeatures& isa_features, + const CompilerOptions& compiler_options) + : CodeGenerator(graph, + kNumberOfGpuRegisters, + kNumberOfFpuRegisters, + 0, // kNumberOfRegisterPairs + ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves), + arraysize(kCoreCalleeSaves)), + ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves), + arraysize(kFpuCalleeSaves)), + compiler_options), + block_labels_(graph->GetArena(), 0), + location_builder_(graph, this), + instruction_visitor_(graph, this), + move_resolver_(graph->GetArena(), this), + isa_features_(isa_features) { + // Save RA (containing the return address) to mimic Quick. + AddAllocatedRegister(Location::RegisterLocation(RA)); +} + +#undef __ +#define __ down_cast<Mips64Assembler*>(GetAssembler())-> +#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, x).Int32Value() + +void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) { + CodeGenerator::Finalize(allocator); +} + +Mips64Assembler* ParallelMoveResolverMIPS64::GetAssembler() const { + return codegen_->GetAssembler(); +} + +void ParallelMoveResolverMIPS64::EmitMove(size_t index) { + MoveOperands* move = moves_.Get(index); + codegen_->MoveLocation(move->GetDestination(), move->GetSource(), move->GetType()); +} + +void ParallelMoveResolverMIPS64::EmitSwap(size_t index) { + MoveOperands* move = moves_.Get(index); + codegen_->SwapLocations(move->GetDestination(), move->GetSource(), move->GetType()); +} + +void ParallelMoveResolverMIPS64::RestoreScratch(int reg) { + // Pop reg + __ Ld(GpuRegister(reg), SP, 0); + __ DecreaseFrameSize(kMips64WordSize); +} + +void ParallelMoveResolverMIPS64::SpillScratch(int reg) { + // Push reg + __ IncreaseFrameSize(kMips64WordSize); + __ Sd(GpuRegister(reg), SP, 0); +} + +void ParallelMoveResolverMIPS64::Exchange(int index1, int index2, bool double_slot) { + LoadOperandType load_type = double_slot ? kLoadDoubleword : kLoadWord; + StoreOperandType store_type = double_slot ? kStoreDoubleword : kStoreWord; + // Allocate a scratch register other than TMP, if available. + // Else, spill V0 (arbitrary choice) and use it as a scratch register (it will be + // automatically unspilled when the scratch scope object is destroyed). + ScratchRegisterScope ensure_scratch(this, TMP, V0, codegen_->GetNumberOfCoreRegisters()); + // If V0 spills onto the stack, SP-relative offsets need to be adjusted. + int stack_offset = ensure_scratch.IsSpilled() ? kMips64WordSize : 0; + __ LoadFromOffset(load_type, + GpuRegister(ensure_scratch.GetRegister()), + SP, + index1 + stack_offset); + __ LoadFromOffset(load_type, + TMP, + SP, + index2 + stack_offset); + __ StoreToOffset(store_type, + GpuRegister(ensure_scratch.GetRegister()), + SP, + index2 + stack_offset); + __ StoreToOffset(store_type, TMP, SP, index1 + stack_offset); +} + +static dwarf::Reg DWARFReg(GpuRegister reg) { + return dwarf::Reg::Mips64Core(static_cast<int>(reg)); +} + +// TODO: mapping of floating-point registers to DWARF + +void CodeGeneratorMIPS64::GenerateFrameEntry() { + __ Bind(&frame_entry_label_); + + bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kMips64) || !IsLeafMethod(); + + if (do_overflow_check) { + __ LoadFromOffset(kLoadWord, + ZERO, + SP, + -static_cast<int32_t>(GetStackOverflowReservedBytes(kMips64))); + RecordPcInfo(nullptr, 0); + } + + // TODO: anything related to T9/GP/GOT/PIC/.so's? + + if (HasEmptyFrame()) { + return; + } + + // Make sure the frame size isn't unreasonably large. Per the various APIs + // it looks like it should always be less than 2GB in size, which allows + // us using 32-bit signed offsets from the stack pointer. + if (GetFrameSize() > 0x7FFFFFFF) + LOG(FATAL) << "Stack frame larger than 2GB"; + + // Spill callee-saved registers. + // Note that their cumulative size is small and they can be indexed using + // 16-bit offsets. + + // TODO: increment/decrement SP in one step instead of two or remove this comment. + + uint32_t ofs = FrameEntrySpillSize(); + __ IncreaseFrameSize(ofs); + + for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) { + GpuRegister reg = kCoreCalleeSaves[i]; + if (allocated_registers_.ContainsCoreRegister(reg)) { + ofs -= kMips64WordSize; + __ Sd(reg, SP, ofs); + __ cfi().RelOffset(DWARFReg(reg), ofs); + } + } + + for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) { + FpuRegister reg = kFpuCalleeSaves[i]; + if (allocated_registers_.ContainsFloatingPointRegister(reg)) { + ofs -= kMips64WordSize; + __ Sdc1(reg, SP, ofs); + // TODO: __ cfi().RelOffset(DWARFReg(reg), ofs); + } + } + + // Allocate the rest of the frame and store the current method pointer + // at its end. + + __ IncreaseFrameSize(GetFrameSize() - FrameEntrySpillSize()); + + static_assert(IsInt<16>(kCurrentMethodStackOffset), + "kCurrentMethodStackOffset must fit into int16_t"); + __ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset); +} + +void CodeGeneratorMIPS64::GenerateFrameExit() { + __ cfi().RememberState(); + + // TODO: anything related to T9/GP/GOT/PIC/.so's? + + if (!HasEmptyFrame()) { + // Deallocate the rest of the frame. + + __ DecreaseFrameSize(GetFrameSize() - FrameEntrySpillSize()); + + // Restore callee-saved registers. + // Note that their cumulative size is small and they can be indexed using + // 16-bit offsets. + + // TODO: increment/decrement SP in one step instead of two or remove this comment. + + uint32_t ofs = 0; + + for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { + FpuRegister reg = kFpuCalleeSaves[i]; + if (allocated_registers_.ContainsFloatingPointRegister(reg)) { + __ Ldc1(reg, SP, ofs); + ofs += kMips64WordSize; + // TODO: __ cfi().Restore(DWARFReg(reg)); + } + } + + for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { + GpuRegister reg = kCoreCalleeSaves[i]; + if (allocated_registers_.ContainsCoreRegister(reg)) { + __ Ld(reg, SP, ofs); + ofs += kMips64WordSize; + __ cfi().Restore(DWARFReg(reg)); + } + } + + DCHECK_EQ(ofs, FrameEntrySpillSize()); + __ DecreaseFrameSize(ofs); + } + + __ Jr(RA); + + __ cfi().RestoreState(); + __ cfi().DefCFAOffset(GetFrameSize()); +} + +void CodeGeneratorMIPS64::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); +} + +void CodeGeneratorMIPS64::MoveLocation(Location destination, + Location source, + Primitive::Type type) { + if (source.Equals(destination)) { + return; + } + + // A valid move can always be inferred from the destination and source + // locations. When moving from and to a register, the argument type can be + // used to generate 32bit instead of 64bit moves. + bool unspecified_type = (type == Primitive::kPrimVoid); + DCHECK_EQ(unspecified_type, false); + + if (destination.IsRegister() || destination.IsFpuRegister()) { + if (unspecified_type) { + HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr; + if (source.IsStackSlot() || + (src_cst != nullptr && (src_cst->IsIntConstant() + || src_cst->IsFloatConstant() + || src_cst->IsNullConstant()))) { + // For stack slots and 32bit constants, a 64bit type is appropriate. + type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat; + } else { + // If the source is a double stack slot or a 64bit constant, a 64bit + // type is appropriate. Else the source is a register, and since the + // type has not been specified, we chose a 64bit type to force a 64bit + // move. + type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble; + } + } + DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) || + (destination.IsRegister() && !Primitive::IsFloatingPointType(type))); + if (source.IsStackSlot() || source.IsDoubleStackSlot()) { + // Move to GPR/FPR from stack + LoadOperandType load_type = source.IsStackSlot() ? kLoadWord : kLoadDoubleword; + if (Primitive::IsFloatingPointType(type)) { + __ LoadFpuFromOffset(load_type, + destination.AsFpuRegister<FpuRegister>(), + SP, + source.GetStackIndex()); + } else { + // TODO: use load_type = kLoadUnsignedWord when type == Primitive::kPrimNot. + __ LoadFromOffset(load_type, + destination.AsRegister<GpuRegister>(), + SP, + source.GetStackIndex()); + } + } else if (source.IsConstant()) { + // Move to GPR/FPR from constant + GpuRegister gpr = AT; + if (!Primitive::IsFloatingPointType(type)) { + gpr = destination.AsRegister<GpuRegister>(); + } + if (type == Primitive::kPrimInt || type == Primitive::kPrimFloat) { + __ LoadConst32(gpr, GetInt32ValueOf(source.GetConstant()->AsConstant())); + } else { + __ LoadConst64(gpr, GetInt64ValueOf(source.GetConstant()->AsConstant())); + } + if (type == Primitive::kPrimFloat) { + __ Mtc1(gpr, destination.AsFpuRegister<FpuRegister>()); + } else if (type == Primitive::kPrimDouble) { + __ Dmtc1(gpr, destination.AsFpuRegister<FpuRegister>()); + } + } else { + if (destination.IsRegister()) { + // Move to GPR from GPR + __ Move(destination.AsRegister<GpuRegister>(), source.AsRegister<GpuRegister>()); + } else { + // Move to FPR from FPR + if (type == Primitive::kPrimFloat) { + __ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>()); + } else { + DCHECK_EQ(type, Primitive::kPrimDouble); + __ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>()); + } + } + } + } else { // The destination is not a register. It must be a stack slot. + DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot()); + if (source.IsRegister() || source.IsFpuRegister()) { + if (unspecified_type) { + if (source.IsRegister()) { + type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong; + } else { + type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble; + } + } + DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) && + (source.IsFpuRegister() == Primitive::IsFloatingPointType(type))); + // Move to stack from GPR/FPR + StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword; + if (source.IsRegister()) { + __ StoreToOffset(store_type, + source.AsRegister<GpuRegister>(), + SP, + destination.GetStackIndex()); + } else { + __ StoreFpuToOffset(store_type, + source.AsFpuRegister<FpuRegister>(), + SP, + destination.GetStackIndex()); + } + } else if (source.IsConstant()) { + // Move to stack from constant + HConstant* src_cst = source.GetConstant(); + StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword; + if (destination.IsStackSlot()) { + __ LoadConst32(TMP, GetInt32ValueOf(src_cst->AsConstant())); + } else { + __ LoadConst64(TMP, GetInt64ValueOf(src_cst->AsConstant())); + } + __ StoreToOffset(store_type, TMP, SP, destination.GetStackIndex()); + } else { + DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot()); + DCHECK_EQ(source.IsDoubleStackSlot(), destination.IsDoubleStackSlot()); + // Move to stack from stack + if (destination.IsStackSlot()) { + __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex()); + __ StoreToOffset(kStoreWord, TMP, SP, destination.GetStackIndex()); + } else { + __ LoadFromOffset(kLoadDoubleword, TMP, SP, source.GetStackIndex()); + __ StoreToOffset(kStoreDoubleword, TMP, SP, destination.GetStackIndex()); + } + } + } +} + +void CodeGeneratorMIPS64::SwapLocations(Location loc1, + Location loc2, + Primitive::Type type ATTRIBUTE_UNUSED) { + DCHECK(!loc1.IsConstant()); + DCHECK(!loc2.IsConstant()); + + if (loc1.Equals(loc2)) { + return; + } + + bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot(); + bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot(); + bool is_fp_reg1 = loc1.IsFpuRegister(); + bool is_fp_reg2 = loc2.IsFpuRegister(); + + if (loc2.IsRegister() && loc1.IsRegister()) { + // Swap 2 GPRs + GpuRegister r1 = loc1.AsRegister<GpuRegister>(); + GpuRegister r2 = loc2.AsRegister<GpuRegister>(); + __ Move(TMP, r2); + __ Move(r2, r1); + __ Move(r1, TMP); + } else if (is_fp_reg2 && is_fp_reg1) { + // Swap 2 FPRs + FpuRegister r1 = loc1.AsFpuRegister<FpuRegister>(); + FpuRegister r2 = loc2.AsFpuRegister<FpuRegister>(); + // TODO: Can MOV.S/MOV.D be used here to save one instruction? + // Need to distinguish float from double, right? + __ Dmfc1(TMP, r2); + __ Dmfc1(AT, r1); + __ Dmtc1(TMP, r1); + __ Dmtc1(AT, r2); + } else if (is_slot1 != is_slot2) { + // Swap GPR/FPR and stack slot + Location reg_loc = is_slot1 ? loc2 : loc1; + Location mem_loc = is_slot1 ? loc1 : loc2; + LoadOperandType load_type = mem_loc.IsStackSlot() ? kLoadWord : kLoadDoubleword; + StoreOperandType store_type = mem_loc.IsStackSlot() ? kStoreWord : kStoreDoubleword; + // TODO: use load_type = kLoadUnsignedWord when type == Primitive::kPrimNot. + __ LoadFromOffset(load_type, TMP, SP, mem_loc.GetStackIndex()); + if (reg_loc.IsFpuRegister()) { + __ StoreFpuToOffset(store_type, + reg_loc.AsFpuRegister<FpuRegister>(), + SP, + mem_loc.GetStackIndex()); + // TODO: review this MTC1/DMTC1 move + if (mem_loc.IsStackSlot()) { + __ Mtc1(TMP, reg_loc.AsFpuRegister<FpuRegister>()); + } else { + DCHECK(mem_loc.IsDoubleStackSlot()); + __ Dmtc1(TMP, reg_loc.AsFpuRegister<FpuRegister>()); + } + } else { + __ StoreToOffset(store_type, reg_loc.AsRegister<GpuRegister>(), SP, mem_loc.GetStackIndex()); + __ Move(reg_loc.AsRegister<GpuRegister>(), TMP); + } + } else if (is_slot1 && is_slot2) { + move_resolver_.Exchange(loc1.GetStackIndex(), + loc2.GetStackIndex(), + loc1.IsDoubleStackSlot()); + } else { + LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2; + } +} + +void CodeGeneratorMIPS64::Move(HInstruction* instruction, + Location location, + HInstruction* move_for) { + LocationSummary* locations = instruction->GetLocations(); + Primitive::Type type = instruction->GetType(); + DCHECK_NE(type, Primitive::kPrimVoid); + + if (instruction->IsCurrentMethod()) { + MoveLocation(location, Location::DoubleStackSlot(kCurrentMethodStackOffset), type); + } else if (locations != nullptr && locations->Out().Equals(location)) { + return; + } else if (instruction->IsIntConstant() + || instruction->IsLongConstant() + || instruction->IsNullConstant()) { + if (location.IsRegister()) { + // Move to GPR from constant + GpuRegister dst = location.AsRegister<GpuRegister>(); + if (instruction->IsNullConstant() || instruction->IsIntConstant()) { + __ LoadConst32(dst, GetInt32ValueOf(instruction->AsConstant())); + } else { + __ LoadConst64(dst, instruction->AsLongConstant()->GetValue()); + } + } else { + DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot()); + // Move to stack from constant + if (location.IsStackSlot()) { + __ LoadConst32(TMP, GetInt32ValueOf(instruction->AsConstant())); + __ StoreToOffset(kStoreWord, TMP, SP, location.GetStackIndex()); + } else { + __ LoadConst64(TMP, instruction->AsLongConstant()->GetValue()); + __ StoreToOffset(kStoreDoubleword, TMP, SP, location.GetStackIndex()); + } + } + } else if (instruction->IsTemporary()) { + Location temp_location = GetTemporaryLocation(instruction->AsTemporary()); + MoveLocation(location, temp_location, type); + } else if (instruction->IsLoadLocal()) { + uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); + if (Primitive::Is64BitType(type)) { + MoveLocation(location, Location::DoubleStackSlot(stack_slot), type); + } else { + MoveLocation(location, Location::StackSlot(stack_slot), type); + } + } else { + DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); + MoveLocation(location, locations->Out(), type); + } +} + +Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const { + Primitive::Type type = load->GetType(); + + switch (type) { + case Primitive::kPrimNot: + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + return Location::StackSlot(GetStackSlot(load->GetLocal())); + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimVoid: + LOG(FATAL) << "Unexpected type " << type; + } + + LOG(FATAL) << "Unreachable"; + return Location::NoLocation(); +} + +void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, GpuRegister value) { + Label done; + GpuRegister card = AT; + GpuRegister temp = TMP; + __ Beqzc(value, &done); + __ LoadFromOffset(kLoadDoubleword, + card, + TR, + Thread::CardTableOffset<kMips64WordSize>().Int32Value()); + __ Dsrl(temp, object, gc::accounting::CardTable::kCardShift); + __ Daddu(temp, card, temp); + __ Sb(card, temp, 0); + __ Bind(&done); +} + +void CodeGeneratorMIPS64::SetupBlockedRegisters(bool is_baseline ATTRIBUTE_UNUSED) const { + // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated. + blocked_core_registers_[ZERO] = true; + blocked_core_registers_[K0] = true; + blocked_core_registers_[K1] = true; + blocked_core_registers_[GP] = true; + blocked_core_registers_[SP] = true; + blocked_core_registers_[RA] = true; + + // AT and TMP(T8) are used as temporary/scratch registers + // (similar to how AT is used by MIPS assemblers). + blocked_core_registers_[AT] = true; + blocked_core_registers_[TMP] = true; + blocked_fpu_registers_[FTMP] = true; + + // Reserve suspend and thread registers. + blocked_core_registers_[S0] = true; + blocked_core_registers_[TR] = true; + + // Reserve T9 for function calls + blocked_core_registers_[T9] = true; + + // TODO: review; anything else? + + // TODO: make these two for's conditional on is_baseline once + // all the issues with register saving/restoring are sorted out. + for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { + blocked_core_registers_[kCoreCalleeSaves[i]] = true; + } + + for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { + blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; + } +} + +Location CodeGeneratorMIPS64::AllocateFreeRegister(Primitive::Type type) const { + if (type == Primitive::kPrimVoid) { + LOG(FATAL) << "Unreachable type " << type; + } + + if (Primitive::IsFloatingPointType(type)) { + size_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfFpuRegisters); + return Location::FpuRegisterLocation(reg); + } else { + size_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfGpuRegisters); + return Location::RegisterLocation(reg); + } +} + +size_t CodeGeneratorMIPS64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ StoreToOffset(kStoreDoubleword, GpuRegister(reg_id), SP, stack_index); + return kMips64WordSize; +} + +size_t CodeGeneratorMIPS64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ LoadFromOffset(kLoadDoubleword, GpuRegister(reg_id), SP, stack_index); + return kMips64WordSize; +} + +size_t CodeGeneratorMIPS64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + __ StoreFpuToOffset(kStoreDoubleword, FpuRegister(reg_id), SP, stack_index); + return kMips64WordSize; +} + +size_t CodeGeneratorMIPS64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + __ LoadFpuFromOffset(kLoadDoubleword, FpuRegister(reg_id), SP, stack_index); + return kMips64WordSize; +} + +void CodeGeneratorMIPS64::DumpCoreRegister(std::ostream& stream, int reg) const { + stream << Mips64ManagedRegister::FromGpuRegister(GpuRegister(reg)); +} + +void CodeGeneratorMIPS64::DumpFloatingPointRegister(std::ostream& stream, int reg) const { + stream << Mips64ManagedRegister::FromFpuRegister(FpuRegister(reg)); +} + +void CodeGeneratorMIPS64::InvokeRuntime(int32_t entry_point_offset, + HInstruction* instruction, + uint32_t dex_pc, + SlowPathCode* slow_path) { + // TODO: anything related to T9/GP/GOT/PIC/.so's? + __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset); + __ Jalr(T9); + RecordPcInfo(instruction, dex_pc, slow_path); + DCHECK(instruction->IsSuspendCheck() + || instruction->IsBoundsCheck() + || instruction->IsNullCheck() + || instruction->IsDivZeroCheck() + || !IsLeafMethod()); +} + +void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, + GpuRegister class_reg) { + __ LoadFromOffset(kLoadWord, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); + __ LoadConst32(AT, mirror::Class::kStatusInitialized); + __ Bltc(TMP, AT, slow_path->GetEntryLabel()); + // TODO: barrier needed? + __ Bind(slow_path->GetExitLabel()); +} + +void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) { + __ Sync(0); // only stype 0 is supported +} + +void InstructionCodeGeneratorMIPS64::GenerateSuspendCheck(HSuspendCheck* instruction, + HBasicBlock* successor) { + SuspendCheckSlowPathMIPS64* slow_path = + new (GetGraph()->GetArena()) SuspendCheckSlowPathMIPS64(instruction, successor); + codegen_->AddSlowPath(slow_path); + + __ LoadFromOffset(kLoadUnsignedHalfword, + TMP, + TR, + Thread::ThreadFlagsOffset<kMips64WordSize>().Int32Value()); + if (successor == nullptr) { + __ Bnezc(TMP, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetReturnLabel()); + } else { + __ Beqzc(TMP, codegen_->GetLabelOf(successor)); + __ B(slow_path->GetEntryLabel()); + // slow_path will return to GetLabelOf(successor). + } +} + +InstructionCodeGeneratorMIPS64::InstructionCodeGeneratorMIPS64(HGraph* graph, + CodeGeneratorMIPS64* codegen) + : HGraphVisitor(graph), + assembler_(codegen->GetAssembler()), + codegen_(codegen) {} + +void LocationsBuilderMIPS64::HandleBinaryOp(HBinaryOperation* instruction) { + DCHECK_EQ(instruction->InputCount(), 2U); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type type = instruction->GetResultType(); + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + HInstruction* right = instruction->InputAt(1); + bool can_use_imm = false; + if (right->IsConstant()) { + int64_t imm = CodeGenerator::GetInt64ValueOf(right->AsConstant()); + if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { + can_use_imm = IsUint<16>(imm); + } else if (instruction->IsAdd()) { + can_use_imm = IsInt<16>(imm); + } else { + DCHECK(instruction->IsSub()); + can_use_imm = IsInt<16>(-imm); + } + } + if (can_use_imm) + locations->SetInAt(1, Location::ConstantLocation(right->AsConstant())); + else + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected " << instruction->DebugName() << " type " << type; + } +} + +void InstructionCodeGeneratorMIPS64::HandleBinaryOp(HBinaryOperation* instruction) { + Primitive::Type type = instruction->GetType(); + LocationSummary* locations = instruction->GetLocations(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + Location rhs_location = locations->InAt(1); + + GpuRegister rhs_reg = ZERO; + int64_t rhs_imm = 0; + bool use_imm = rhs_location.IsConstant(); + if (use_imm) { + rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()); + } else { + rhs_reg = rhs_location.AsRegister<GpuRegister>(); + } + + if (instruction->IsAnd()) { + if (use_imm) + __ Andi(dst, lhs, rhs_imm); + else + __ And(dst, lhs, rhs_reg); + } else if (instruction->IsOr()) { + if (use_imm) + __ Ori(dst, lhs, rhs_imm); + else + __ Or(dst, lhs, rhs_reg); + } else if (instruction->IsXor()) { + if (use_imm) + __ Xori(dst, lhs, rhs_imm); + else + __ Xor(dst, lhs, rhs_reg); + } else if (instruction->IsAdd()) { + if (type == Primitive::kPrimInt) { + if (use_imm) + __ Addiu(dst, lhs, rhs_imm); + else + __ Addu(dst, lhs, rhs_reg); + } else { + if (use_imm) + __ Daddiu(dst, lhs, rhs_imm); + else + __ Daddu(dst, lhs, rhs_reg); + } + } else { + DCHECK(instruction->IsSub()); + if (type == Primitive::kPrimInt) { + if (use_imm) + __ Addiu(dst, lhs, -rhs_imm); + else + __ Subu(dst, lhs, rhs_reg); + } else { + if (use_imm) + __ Daddiu(dst, lhs, -rhs_imm); + else + __ Dsubu(dst, lhs, rhs_reg); + } + } + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>(); + FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>(); + if (instruction->IsAdd()) { + if (type == Primitive::kPrimFloat) + __ AddS(dst, lhs, rhs); + else + __ AddD(dst, lhs, rhs); + } else if (instruction->IsSub()) { + if (type == Primitive::kPrimFloat) + __ SubS(dst, lhs, rhs); + else + __ SubD(dst, lhs, rhs); + } else { + LOG(FATAL) << "Unexpected floating-point binary operation"; + } + break; + } + default: + LOG(FATAL) << "Unexpected binary operation type " << type; + } +} + +void LocationsBuilderMIPS64::HandleShift(HBinaryOperation* instr) { + DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr()); + + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + Primitive::Type type = instr->GetResultType(); + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + break; + } + default: + LOG(FATAL) << "Unexpected shift type " << type; + } +} + +void InstructionCodeGeneratorMIPS64::HandleShift(HBinaryOperation* instr) { + DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr()); + LocationSummary* locations = instr->GetLocations(); + Primitive::Type type = instr->GetType(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + Location rhs_location = locations->InAt(1); + + GpuRegister rhs_reg = ZERO; + int64_t rhs_imm = 0; + bool use_imm = rhs_location.IsConstant(); + if (use_imm) { + rhs_imm = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()); + } else { + rhs_reg = rhs_location.AsRegister<GpuRegister>(); + } + + if (use_imm) { + uint32_t shift_value = (type == Primitive::kPrimInt) + ? static_cast<uint32_t>(rhs_imm & kMaxIntShiftValue) + : static_cast<uint32_t>(rhs_imm & kMaxLongShiftValue); + + if (type == Primitive::kPrimInt) { + if (instr->IsShl()) { + __ Sll(dst, lhs, shift_value); + } else if (instr->IsShr()) { + __ Sra(dst, lhs, shift_value); + } else { + __ Srl(dst, lhs, shift_value); + } + } else { + if (shift_value < 32) { + if (instr->IsShl()) { + __ Dsll(dst, lhs, shift_value); + } else if (instr->IsShr()) { + __ Dsra(dst, lhs, shift_value); + } else { + __ Dsrl(dst, lhs, shift_value); + } + } else { + shift_value -= 32; + if (instr->IsShl()) { + __ Dsll32(dst, lhs, shift_value); + } else if (instr->IsShr()) { + __ Dsra32(dst, lhs, shift_value); + } else { + __ Dsrl32(dst, lhs, shift_value); + } + } + } + } else { + if (type == Primitive::kPrimInt) { + if (instr->IsShl()) { + __ Sllv(dst, lhs, rhs_reg); + } else if (instr->IsShr()) { + __ Srav(dst, lhs, rhs_reg); + } else { + __ Srlv(dst, lhs, rhs_reg); + } + } else { + if (instr->IsShl()) { + __ Dsllv(dst, lhs, rhs_reg); + } else if (instr->IsShr()) { + __ Dsrav(dst, lhs, rhs_reg); + } else { + __ Dsrlv(dst, lhs, rhs_reg); + } + } + } + break; + } + default: + LOG(FATAL) << "Unexpected shift operation type " << type; + } +} + +void LocationsBuilderMIPS64::VisitAdd(HAdd* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitAdd(HAdd* instruction) { + HandleBinaryOp(instruction); +} + +void LocationsBuilderMIPS64::VisitAnd(HAnd* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitAnd(HAnd* instruction) { + HandleBinaryOp(instruction); +} + +void LocationsBuilderMIPS64::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (Primitive::IsFloatingPointType(instruction->GetType())) { + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + } else { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } +} + +void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + Location index = locations->InAt(1); + Primitive::Type type = instruction->GetType(); + + switch (type) { + case Primitive::kPrimBoolean: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); + } else { + __ Daddu(TMP, obj, index.AsRegister<GpuRegister>()); + __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ LoadFromOffset(kLoadSignedByte, out, obj, offset); + } else { + __ Daddu(TMP, obj, index.AsRegister<GpuRegister>()); + __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimShort: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2); + __ Daddu(TMP, obj, TMP); + __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2); + __ Daddu(TMP, obj, TMP); + __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + LoadOperandType load_type = (type == Primitive::kPrimNot) ? kLoadUnsignedWord : kLoadWord; + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ LoadFromOffset(load_type, out, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); + __ Daddu(TMP, obj, TMP); + __ LoadFromOffset(load_type, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ LoadFromOffset(kLoadDoubleword, out, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); + __ Daddu(TMP, obj, TMP); + __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimFloat: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); + FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ LoadFpuFromOffset(kLoadWord, out, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); + __ Daddu(TMP, obj, TMP); + __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimDouble: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); + FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); + __ Daddu(TMP, obj, TMP); + __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset); + } + break; + } + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + UNREACHABLE(); + } + codegen_->MaybeRecordImplicitNullCheck(instruction); +} + +void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = instruction->GetLocations(); + uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + __ LoadFromOffset(kLoadWord, out, obj, offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); +} + +void LocationsBuilderMIPS64::VisitArraySet(HArraySet* instruction) { + Primitive::Type value_type = instruction->GetComponentType(); + bool is_object = value_type == Primitive::kPrimNot; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( + instruction, + is_object ? LocationSummary::kCall : LocationSummary::kNoCall); + if (is_object) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (Primitive::IsFloatingPointType(instruction->InputAt(2)->GetType())) { + locations->SetInAt(2, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(2, Location::RequiresRegister()); + } + } +} + +void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + Location index = locations->InAt(1); + Primitive::Type value_type = instruction->GetComponentType(); + bool needs_runtime_call = locations->WillCall(); + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + + switch (value_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); + GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; + __ StoreToOffset(kStoreByte, value, obj, offset); + } else { + __ Daddu(TMP, obj, index.AsRegister<GpuRegister>()); + __ StoreToOffset(kStoreByte, value, TMP, data_offset); + } + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); + GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; + __ StoreToOffset(kStoreHalfword, value, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2); + __ Daddu(TMP, obj, TMP); + __ StoreToOffset(kStoreHalfword, value, TMP, data_offset); + } + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + if (!needs_runtime_call) { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ StoreToOffset(kStoreWord, value, obj, offset); + } else { + DCHECK(index.IsRegister()) << index; + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); + __ Daddu(TMP, obj, TMP); + __ StoreToOffset(kStoreWord, value, TMP, data_offset); + } + codegen_->MaybeRecordImplicitNullCheck(instruction); + if (needs_write_barrier) { + DCHECK_EQ(value_type, Primitive::kPrimNot); + codegen_->MarkGCCard(obj, value); + } + } else { + DCHECK_EQ(value_type, Primitive::kPrimNot); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), + instruction, + instruction->GetDexPc(), + nullptr); + } + break; + } + + case Primitive::kPrimLong: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); + GpuRegister value = locations->InAt(2).AsRegister<GpuRegister>(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ StoreToOffset(kStoreDoubleword, value, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); + __ Daddu(TMP, obj, TMP); + __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset); + } + break; + } + + case Primitive::kPrimFloat: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); + FpuRegister value = locations->InAt(2).AsFpuRegister<FpuRegister>(); + DCHECK(locations->InAt(2).IsFpuRegister()); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ StoreFpuToOffset(kStoreWord, value, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); + __ Daddu(TMP, obj, TMP); + __ StoreFpuToOffset(kStoreWord, value, TMP, data_offset); + } + break; + } + + case Primitive::kPrimDouble: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); + FpuRegister value = locations->InAt(2).AsFpuRegister<FpuRegister>(); + DCHECK(locations->InAt(2).IsFpuRegister()); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + __ StoreFpuToOffset(kStoreDoubleword, value, obj, offset); + } else { + __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); + __ Daddu(TMP, obj, TMP); + __ StoreFpuToOffset(kStoreDoubleword, value, TMP, data_offset); + } + break; + } + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + UNREACHABLE(); + } + + // Ints and objects are handled in the switch. + if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } +} + +void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + BoundsCheckSlowPathMIPS64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathMIPS64( + instruction, + locations->InAt(0), + locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + GpuRegister index = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister length = locations->InAt(1).AsRegister<GpuRegister>(); + + // length is limited by the maximum positive signed 32-bit integer. + // Unsigned comparison of length and index checks for index < 0 + // and for length <= index simultaneously. + // Mips R6 requires lhs != rhs for compact branches. + if (index == length) { + __ B(slow_path->GetEntryLabel()); + } else { + __ Bgeuc(index, length, slow_path->GetEntryLabel()); + } +} + +void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( + instruction, + LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) { + LocationSummary* locations = instruction->GetLocations(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); + GpuRegister obj_cls = locations->GetTemp(0).AsRegister<GpuRegister>(); + + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathMIPS64( + instruction, + locations->InAt(1), + Location::RegisterLocation(obj_cls), + instruction->GetDexPc()); + codegen_->AddSlowPath(slow_path); + + // TODO: avoid this check if we know obj is not null. + __ Beqzc(obj, slow_path->GetExitLabel()); + // Compare the class of `obj` with `cls`. + __ LoadFromOffset(kLoadUnsignedWord, obj_cls, obj, mirror::Object::ClassOffset().Int32Value()); + __ Bnec(obj_cls, cls, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); +} + +void LocationsBuilderMIPS64::VisitClinitCheck(HClinitCheck* check) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + if (check->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorMIPS64::VisitClinitCheck(HClinitCheck* check) { + // We assume the class is not null. + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64( + check->GetLoadClass(), + check, + check->GetDexPc(), + true); + codegen_->AddSlowPath(slow_path); + GenerateClassInitializationCheck(slow_path, + check->GetLocations()->InAt(0).AsRegister<GpuRegister>()); +} + +void LocationsBuilderMIPS64::VisitCompare(HCompare* compare) { + Primitive::Type in_type = compare->InputAt(0)->GetType(); + + LocationSummary::CallKind call_kind = Primitive::IsFloatingPointType(in_type) + ? LocationSummary::kCall + : LocationSummary::kNoCall; + + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, call_kind); + + switch (in_type) { + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); + locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); + break; + } + + default: + LOG(FATAL) << "Unexpected type for compare operation " << in_type; + } +} + +void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Primitive::Type in_type = instruction->InputAt(0)->GetType(); + + // 0 if: left == right + // 1 if: left > right + // -1 if: left < right + switch (in_type) { + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>(); + // TODO: more efficient (direct) comparison with a constant + __ Slt(TMP, lhs, rhs); + __ Slt(dst, rhs, lhs); + __ Subu(dst, dst, TMP); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + int32_t entry_point_offset; + if (in_type == Primitive::kPrimFloat) { + entry_point_offset = instruction->IsGtBias() ? QUICK_ENTRY_POINT(pCmpgFloat) + : QUICK_ENTRY_POINT(pCmplFloat); + } else { + entry_point_offset = instruction->IsGtBias() ? QUICK_ENTRY_POINT(pCmpgDouble) + : QUICK_ENTRY_POINT(pCmplDouble); + } + codegen_->InvokeRuntime(entry_point_offset, instruction, instruction->GetDexPc(), nullptr); + break; + } + + default: + LOG(FATAL) << "Unimplemented compare type " << in_type; + } +} + +void LocationsBuilderMIPS64::VisitCondition(HCondition* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (instruction->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } +} + +void InstructionCodeGeneratorMIPS64::VisitCondition(HCondition* instruction) { + if (!instruction->NeedsMaterialization()) { + return; + } + + LocationSummary* locations = instruction->GetLocations(); + + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + Location rhs_location = locations->InAt(1); + + GpuRegister rhs_reg = ZERO; + int64_t rhs_imm = 0; + bool use_imm = rhs_location.IsConstant(); + if (use_imm) { + rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()); + } else { + rhs_reg = rhs_location.AsRegister<GpuRegister>(); + } + + IfCondition if_cond = instruction->GetCondition(); + + switch (if_cond) { + case kCondEQ: + case kCondNE: + if (use_imm && IsUint<16>(rhs_imm)) { + __ Xori(dst, lhs, rhs_imm); + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst32(rhs_reg, rhs_imm); + } + __ Xor(dst, lhs, rhs_reg); + } + if (if_cond == kCondEQ) { + __ Sltiu(dst, dst, 1); + } else { + __ Sltu(dst, ZERO, dst); + } + break; + + case kCondLT: + case kCondGE: + if (use_imm && IsInt<16>(rhs_imm)) { + __ Slti(dst, lhs, rhs_imm); + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst32(rhs_reg, rhs_imm); + } + __ Slt(dst, lhs, rhs_reg); + } + if (if_cond == kCondGE) { + // Simulate lhs >= rhs via !(lhs < rhs) since there's + // only the slt instruction but no sge. + __ Xori(dst, dst, 1); + } + break; + + case kCondLE: + case kCondGT: + if (use_imm && IsInt<16>(rhs_imm + 1)) { + // Simulate lhs <= rhs via lhs < rhs + 1. + __ Slti(dst, lhs, rhs_imm + 1); + if (if_cond == kCondGT) { + // Simulate lhs > rhs via !(lhs <= rhs) since there's + // only the slti instruction but no sgti. + __ Xori(dst, dst, 1); + } + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst32(rhs_reg, rhs_imm); + } + __ Slt(dst, rhs_reg, lhs); + if (if_cond == kCondLE) { + // Simulate lhs <= rhs via !(rhs < lhs) since there's + // only the slt instruction but no sle. + __ Xori(dst, dst, 1); + } + } + break; + } +} + +void LocationsBuilderMIPS64::VisitDiv(HDiv* div) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); + switch (div->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected div type " << div->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::VisitDiv(HDiv* instruction) { + Primitive::Type type = instruction->GetType(); + LocationSummary* locations = instruction->GetLocations(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>(); + if (type == Primitive::kPrimInt) + __ DivR6(dst, lhs, rhs); + else + __ Ddiv(dst, lhs, rhs); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>(); + FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>(); + if (type == Primitive::kPrimFloat) + __ DivS(dst, lhs, rhs); + else + __ DivD(dst, lhs, rhs); + break; + } + default: + LOG(FATAL) << "Unexpected div type " << type; + } +} + +void LocationsBuilderMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) { + SlowPathCodeMIPS64* slow_path = + new (GetGraph()->GetArena()) DivZeroCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + Location value = instruction->GetLocations()->InAt(0); + + Primitive::Type type = instruction->GetType(); + + if ((type != Primitive::kPrimInt) && (type != Primitive::kPrimLong)) { + LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck."; + } + + if (value.IsConstant()) { + int64_t divisor = codegen_->GetInt64ValueOf(value.GetConstant()->AsConstant()); + if (divisor == 0) { + __ B(slow_path->GetEntryLabel()); + } else { + // A division by a non-null constant is valid. We don't need to perform + // any check, so simply fall through. + } + } else { + __ Beqzc(value.AsRegister<GpuRegister>(), slow_path->GetEntryLabel()); + } +} + +void LocationsBuilderMIPS64::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorMIPS64::VisitDoubleConstant(HDoubleConstant* cst ATTRIBUTE_UNUSED) { + // Will be generated at use site. +} + +void LocationsBuilderMIPS64::VisitExit(HExit* exit) { + exit->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { +} + +void LocationsBuilderMIPS64::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorMIPS64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { + // Will be generated at use site. +} + +void LocationsBuilderMIPS64::VisitGoto(HGoto* got) { + got->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitGoto(HGoto* got) { + HBasicBlock* successor = got->GetSuccessor(); + DCHECK(!successor->IsExitBlock()); + HBasicBlock* block = got->GetBlock(); + HInstruction* previous = got->GetPrevious(); + HLoopInformation* info = block->GetLoopInformation(); + + if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { + codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); + GenerateSuspendCheck(info->GetSuspendCheck(), successor); + return; + } + if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { + GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); + } + if (!codegen_->GoesToNextBlock(block, successor)) { + __ B(codegen_->GetLabelOf(successor)); + } +} + +void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruction, + Label* true_target, + Label* false_target, + Label* always_true_target) { + HInstruction* cond = instruction->InputAt(0); + HCondition* condition = cond->AsCondition(); + + if (cond->IsIntConstant()) { + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (always_true_target != nullptr) { + __ B(always_true_target); + } + return; + } else { + DCHECK_EQ(cond_value, 0); + } + } else if (!cond->IsCondition() || condition->NeedsMaterialization()) { + // The condition instruction has been materialized, compare the output to 0. + Location cond_val = instruction->GetLocations()->InAt(0); + DCHECK(cond_val.IsRegister()); + __ Bnezc(cond_val.AsRegister<GpuRegister>(), true_target); + } else { + // The condition instruction has not been materialized, use its inputs as + // the comparison and its condition as the branch condition. + GpuRegister lhs = condition->GetLocations()->InAt(0).AsRegister<GpuRegister>(); + Location rhs_location = condition->GetLocations()->InAt(1); + GpuRegister rhs_reg = ZERO; + int32_t rhs_imm = 0; + bool use_imm = rhs_location.IsConstant(); + if (use_imm) { + rhs_imm = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()); + } else { + rhs_reg = rhs_location.AsRegister<GpuRegister>(); + } + + IfCondition if_cond = condition->GetCondition(); + if (use_imm && rhs_imm == 0) { + switch (if_cond) { + case kCondEQ: + __ Beqzc(lhs, true_target); + break; + case kCondNE: + __ Bnezc(lhs, true_target); + break; + case kCondLT: + __ Bltzc(lhs, true_target); + break; + case kCondGE: + __ Bgezc(lhs, true_target); + break; + case kCondLE: + __ Blezc(lhs, true_target); + break; + case kCondGT: + __ Bgtzc(lhs, true_target); + break; + } + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst32(rhs_reg, rhs_imm); + } + // It looks like we can get here with lhs == rhs. Should that be possible at all? + // Mips R6 requires lhs != rhs for compact branches. + if (lhs == rhs_reg) { + DCHECK(!use_imm); + switch (if_cond) { + case kCondEQ: + case kCondGE: + case kCondLE: + // if lhs == rhs for a positive condition, then it is a branch + __ B(true_target); + break; + case kCondNE: + case kCondLT: + case kCondGT: + // if lhs == rhs for a negative condition, then it is a NOP + break; + } + } else { + switch (if_cond) { + case kCondEQ: + __ Beqc(lhs, rhs_reg, true_target); + break; + case kCondNE: + __ Bnec(lhs, rhs_reg, true_target); + break; + case kCondLT: + __ Bltc(lhs, rhs_reg, true_target); + break; + case kCondGE: + __ Bgec(lhs, rhs_reg, true_target); + break; + case kCondLE: + __ Bgec(rhs_reg, lhs, true_target); + break; + case kCondGT: + __ Bltc(rhs_reg, lhs, true_target); + break; + } + } + } + } + if (false_target != nullptr) { + __ B(false_target); + } +} + +void LocationsBuilderMIPS64::VisitIf(HIf* if_instr) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); + HInstruction* cond = if_instr->InputAt(0); + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { + locations->SetInAt(0, Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorMIPS64::VisitIf(HIf* if_instr) { + Label* true_target = codegen_->GetLabelOf(if_instr->IfTrueSuccessor()); + Label* false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); + Label* always_true_target = true_target; + if (codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + always_true_target = nullptr; + } + if (codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { + false_target = nullptr; + } + GenerateTestAndBranch(if_instr, true_target, false_target, always_true_target); +} + +void LocationsBuilderMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) { + LocationSummary* locations = new (GetGraph()->GetArena()) + LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); + HInstruction* cond = deoptimize->InputAt(0); + DCHECK(cond->IsCondition()); + if (cond->AsCondition()->NeedsMaterialization()) { + locations->SetInAt(0, Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) { + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) + DeoptimizationSlowPathMIPS64(deoptimize); + codegen_->AddSlowPath(slow_path); + Label* slow_path_entry = slow_path->GetEntryLabel(); + GenerateTestAndBranch(deoptimize, slow_path_entry, nullptr, slow_path_entry); +} + +void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction, + const FieldInfo& field_info ATTRIBUTE_UNUSED) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (Primitive::IsFloatingPointType(instruction->GetType())) { + locations->SetOut(Location::RequiresFpuRegister()); + } else { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } +} + +void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction, + const FieldInfo& field_info) { + Primitive::Type type = field_info.GetFieldType(); + LocationSummary* locations = instruction->GetLocations(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + LoadOperandType load_type = kLoadUnsignedByte; + switch (type) { + case Primitive::kPrimBoolean: + load_type = kLoadUnsignedByte; + break; + case Primitive::kPrimByte: + load_type = kLoadSignedByte; + break; + case Primitive::kPrimShort: + load_type = kLoadSignedHalfword; + break; + case Primitive::kPrimChar: + load_type = kLoadUnsignedHalfword; + break; + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + load_type = kLoadWord; + break; + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + load_type = kLoadDoubleword; + break; + case Primitive::kPrimNot: + load_type = kLoadUnsignedWord; + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } + if (!Primitive::IsFloatingPointType(type)) { + DCHECK(locations->Out().IsRegister()); + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + __ LoadFromOffset(load_type, dst, obj, field_info.GetFieldOffset().Uint32Value()); + } else { + DCHECK(locations->Out().IsFpuRegister()); + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + __ LoadFpuFromOffset(load_type, dst, obj, field_info.GetFieldOffset().Uint32Value()); + } + + codegen_->MaybeRecordImplicitNullCheck(instruction); + // TODO: memory barrier? +} + +void LocationsBuilderMIPS64::HandleFieldSet(HInstruction* instruction, + const FieldInfo& field_info ATTRIBUTE_UNUSED) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (Primitive::IsFloatingPointType(instruction->InputAt(1)->GetType())) { + locations->SetInAt(1, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction, + const FieldInfo& field_info) { + Primitive::Type type = field_info.GetFieldType(); + LocationSummary* locations = instruction->GetLocations(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + StoreOperandType store_type = kStoreByte; + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + store_type = kStoreByte; + break; + case Primitive::kPrimShort: + case Primitive::kPrimChar: + store_type = kStoreHalfword; + break; + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimNot: + store_type = kStoreWord; + break; + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + store_type = kStoreDoubleword; + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << type; + UNREACHABLE(); + } + if (!Primitive::IsFloatingPointType(type)) { + DCHECK(locations->InAt(1).IsRegister()); + GpuRegister src = locations->InAt(1).AsRegister<GpuRegister>(); + __ StoreToOffset(store_type, src, obj, field_info.GetFieldOffset().Uint32Value()); + } else { + DCHECK(locations->InAt(1).IsFpuRegister()); + FpuRegister src = locations->InAt(1).AsFpuRegister<FpuRegister>(); + __ StoreFpuToOffset(store_type, src, obj, field_info.GetFieldOffset().Uint32Value()); + } + + codegen_->MaybeRecordImplicitNullCheck(instruction); + // TODO: memory barriers? + if (CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1))) { + DCHECK(locations->InAt(1).IsRegister()); + GpuRegister src = locations->InAt(1).AsRegister<GpuRegister>(); + codegen_->MarkGCCard(obj, src); + } +} + +void LocationsBuilderMIPS64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + HandleFieldGet(instruction, instruction->GetFieldInfo()); +} + +void InstructionCodeGeneratorMIPS64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + HandleFieldGet(instruction, instruction->GetFieldInfo()); +} + +void LocationsBuilderMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + HandleFieldSet(instruction, instruction->GetFieldInfo()); +} + +void InstructionCodeGeneratorMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + HandleFieldSet(instruction, instruction->GetFieldInfo()); +} + +void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { + LocationSummary::CallKind call_kind = + instruction->IsClassFinal() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + // The output does overlap inputs. + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) { + LocationSummary* locations = instruction->GetLocations(); + GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + + Label done; + + // Return 0 if `obj` is null. + // TODO: Avoid this check if we know `obj` is not null. + __ Move(out, ZERO); + __ Beqzc(obj, &done); + + // Compare the class of `obj` with `cls`. + __ LoadFromOffset(kLoadUnsignedWord, out, obj, mirror::Object::ClassOffset().Int32Value()); + if (instruction->IsClassFinal()) { + // Classes must be equal for the instanceof to succeed. + __ Xor(out, out, cls); + __ Sltiu(out, out, 1); + } else { + // If the classes are not equal, we go into a slow path. + DCHECK(locations->OnlyCallsOnSlowPath()); + SlowPathCodeMIPS64* slow_path = + new (GetGraph()->GetArena()) TypeCheckSlowPathMIPS64(instruction, + locations->InAt(1), + locations->Out(), + instruction->GetDexPc()); + codegen_->AddSlowPath(slow_path); + __ Bnec(out, cls, slow_path->GetEntryLabel()); + __ LoadConst32(out, 1); + __ Bind(slow_path->GetExitLabel()); + } + + __ Bind(&done); +} + +void LocationsBuilderMIPS64::VisitIntConstant(HIntConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorMIPS64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { + // Will be generated at use site. +} + +void LocationsBuilderMIPS64::VisitNullConstant(HNullConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorMIPS64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { + // Will be generated at use site. +} + +void LocationsBuilderMIPS64::HandleInvoke(HInvoke* invoke) { + InvokeDexCallingConventionVisitorMIPS64 calling_convention_visitor; + CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor); +} + +void LocationsBuilderMIPS64::VisitInvokeInterface(HInvokeInterface* invoke) { + HandleInvoke(invoke); + // The register T0 is required to be used for the hidden argument in + // art_quick_imt_conflict_trampoline, so add the hidden argument. + invoke->GetLocations()->AddTemp(Location::RegisterLocation(T0)); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokeInterface(HInvokeInterface* invoke) { + // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError. + GpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<GpuRegister>(); + uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset( + invoke->GetImtIndex() % mirror::Class::kImtSize, kMips64PointerSize).Uint32Value(); + Location receiver = invoke->GetLocations()->InAt(0); + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64WordSize); + + // Set the hidden argument. + __ LoadConst32(invoke->GetLocations()->GetTemp(1).AsRegister<GpuRegister>(), + invoke->GetDexMethodIndex()); + + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ LoadFromOffset(kLoadUnsignedWord, temp, SP, receiver.GetStackIndex()); + __ LoadFromOffset(kLoadUnsignedWord, temp, temp, class_offset); + } else { + __ LoadFromOffset(kLoadUnsignedWord, temp, receiver.AsRegister<GpuRegister>(), class_offset); + } + codegen_->MaybeRecordImplicitNullCheck(invoke); + // temp = temp->GetImtEntryAt(method_offset); + __ LoadFromOffset(kLoadDoubleword, temp, temp, method_offset); + // T9 = temp->GetEntryPoint(); + __ LoadFromOffset(kLoadDoubleword, T9, temp, entry_point.Int32Value()); + // T9(); + __ Jalr(T9); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + // TODO intrinsic function + HandleInvoke(invoke); +} + +void LocationsBuilderMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); + + // TODO - intrinsic function + HandleInvoke(invoke); + + // While SetupBlockedRegisters() blocks registers S2-S8 due to their + // clobbering somewhere else, reduce further register pressure by avoiding + // allocation of a register for the current method pointer like on x86 baseline. + // TODO: remove this once all the issues with register saving/restoring are + // sorted out. + LocationSummary* locations = invoke->GetLocations(); + Location location = locations->InAt(invoke->GetCurrentMethodInputIndex()); + if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) { + locations->SetInAt(invoke->GetCurrentMethodInputIndex(), Location::NoLocation()); + } +} + +static bool TryGenerateIntrinsicCode(HInvoke* invoke, + CodeGeneratorMIPS64* codegen ATTRIBUTE_UNUSED) { + if (invoke->GetLocations()->Intrinsified()) { + // TODO - intrinsic function + return true; + } + return false; +} + +void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { + // All registers are assumed to be correctly set up per the calling convention. + + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + + if (invoke->IsStringInit()) { + GpuRegister reg = temp.AsRegister<GpuRegister>(); + // temp = thread->string_init_entrypoint + __ LoadFromOffset(kLoadDoubleword, + reg, + TR, + invoke->GetStringInitOffset()); + // T9 = temp->entry_point_from_quick_compiled_code_; + __ LoadFromOffset(kLoadDoubleword, + T9, + reg, + ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kMips64WordSize).Int32Value()); + // T9() + __ Jalr(T9); + } else if (invoke->IsRecursive()) { + __ Jalr(&frame_entry_label_, T9); + } else { + Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex()); + GpuRegister reg = temp.AsRegister<GpuRegister>(); + GpuRegister method_reg; + if (current_method.IsRegister()) { + method_reg = current_method.AsRegister<GpuRegister>(); + } else { + // TODO: use the appropriate DCHECK() here if possible. + // DCHECK(invoke->GetLocations()->Intrinsified()); + DCHECK(!current_method.IsValid()); + method_reg = reg; + __ Ld(reg, SP, kCurrentMethodStackOffset); + } + + // temp = temp->dex_cache_resolved_methods_; + __ LoadFromOffset(kLoadUnsignedWord, + reg, + method_reg, + ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); + // temp = temp[index_in_cache] + __ LoadFromOffset(kLoadDoubleword, + reg, + reg, + CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())); + // T9 = temp[offset_of_quick_compiled_code] + __ LoadFromOffset(kLoadDoubleword, + T9, + reg, + ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kMips64WordSize).Int32Value()); + // T9() + __ Jalr(T9); + } + + DCHECK(!IsLeafMethod()); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); + + if (TryGenerateIntrinsicCode(invoke, codegen_)) { + return; + } + + LocationSummary* locations = invoke->GetLocations(); + codegen_->GenerateStaticOrDirectCall(invoke, + locations->HasTemps() + ? locations->GetTemp(0) + : Location::NoLocation()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + // TODO: Try to generate intrinsics code. + LocationSummary* locations = invoke->GetLocations(); + Location receiver = locations->InAt(0); + GpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<GpuRegister>(); + size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( + invoke->GetVTableIndex(), kMips64PointerSize).SizeValue(); + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64WordSize); + + // temp = object->GetClass(); + DCHECK(receiver.IsRegister()); + __ LoadFromOffset(kLoadUnsignedWord, temp, receiver.AsRegister<GpuRegister>(), class_offset); + codegen_->MaybeRecordImplicitNullCheck(invoke); + // temp = temp->GetMethodAt(method_offset); + __ LoadFromOffset(kLoadDoubleword, temp, temp, method_offset); + // T9 = temp->GetEntryPoint(); + __ LoadFromOffset(kLoadDoubleword, T9, temp, entry_point.Int32Value()); + // T9(); + __ Jalr(T9); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { + LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { + LocationSummary* locations = cls->GetLocations(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>(); + if (cls->IsReferrersClass()) { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + __ LoadFromOffset(kLoadUnsignedWord, out, current_method, + ArtMethod::DeclaringClassOffset().Int32Value()); + } else { + DCHECK(cls->CanCallRuntime()); + __ LoadFromOffset(kLoadUnsignedWord, out, current_method, + ArtMethod::DexCacheResolvedTypesOffset().Int32Value()); + __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64( + cls, + cls, + cls->GetDexPc(), + cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + __ Beqzc(out, slow_path->GetEntryLabel()); + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); + } + } +} + +void LocationsBuilderMIPS64::VisitLoadException(HLoadException* load) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadException(HLoadException* load) { + GpuRegister out = load->GetLocations()->Out().AsRegister<GpuRegister>(); + __ LoadFromOffset(kLoadUnsignedWord, out, TR, Thread::ExceptionOffset<kMips64WordSize>().Int32Value()); + __ StoreToOffset(kStoreWord, ZERO, TR, Thread::ExceptionOffset<kMips64WordSize>().Int32Value()); +} + +void LocationsBuilderMIPS64::VisitLoadLocal(HLoadLocal* load) { + load->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { + // Nothing to do, this is driven by the code generator. +} + +void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) { + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = load->GetLocations(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>(); + __ LoadFromOffset(kLoadUnsignedWord, out, current_method, + ArtMethod::DeclaringClassOffset().Int32Value()); + __ LoadFromOffset(kLoadUnsignedWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); + __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); + __ Beqzc(out, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); +} + +void LocationsBuilderMIPS64::VisitLocal(HLocal* local) { + local->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitLocal(HLocal* local) { + DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); +} + +void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorMIPS64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { + // Will be generated at use site. +} + +void LocationsBuilderMIPS64::VisitMonitorOperation(HMonitorOperation* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); +} + +void InstructionCodeGeneratorMIPS64::VisitMonitorOperation(HMonitorOperation* instruction) { + codegen_->InvokeRuntime(instruction->IsEnter() + ? QUICK_ENTRY_POINT(pLockObject) + : QUICK_ENTRY_POINT(pUnlockObject), + instruction, + instruction->GetDexPc(), + nullptr); + CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); +} + +void LocationsBuilderMIPS64::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::VisitMul(HMul* instruction) { + Primitive::Type type = instruction->GetType(); + LocationSummary* locations = instruction->GetLocations(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>(); + if (type == Primitive::kPrimInt) + __ MulR6(dst, lhs, rhs); + else + __ Dmul(dst, lhs, rhs); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>(); + FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>(); + if (type == Primitive::kPrimFloat) + __ MulS(dst, lhs, rhs); + else + __ MulD(dst, lhs, rhs); + break; + } + default: + LOG(FATAL) << "Unexpected mul type " << type; + } +} + +void LocationsBuilderMIPS64::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorMIPS64::VisitNeg(HNeg* instruction) { + Primitive::Type type = instruction->GetType(); + LocationSummary* locations = instruction->GetLocations(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>(); + if (type == Primitive::kPrimInt) + __ Subu(dst, ZERO, src); + else + __ Dsubu(dst, ZERO, src); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + FpuRegister src = locations->InAt(0).AsFpuRegister<FpuRegister>(); + if (type == Primitive::kPrimFloat) + __ NegS(dst, src); + else + __ NegD(dst, src); + break; + } + default: + LOG(FATAL) << "Unexpected neg type " << type; + } +} + +void LocationsBuilderMIPS64::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = instruction->GetLocations(); + // Move an uint16_t value to a register. + __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex()); + codegen_->InvokeRuntime( + GetThreadOffset<kMips64WordSize>(instruction->GetEntrypoint()).Int32Value(), + instruction, + instruction->GetDexPc(), + nullptr); + CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); +} + +void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = instruction->GetLocations(); + // Move an uint16_t value to a register. + __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex()); + codegen_->InvokeRuntime( + GetThreadOffset<kMips64WordSize>(instruction->GetEntrypoint()).Int32Value(), + instruction, + instruction->GetDexPc(), + nullptr); + CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); +} + +void LocationsBuilderMIPS64::VisitNot(HNot* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorMIPS64::VisitNot(HNot* instruction) { + Primitive::Type type = instruction->GetType(); + LocationSummary* locations = instruction->GetLocations(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>(); + __ Nor(dst, src, ZERO); + break; + } + + default: + LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType(); + } +} + +void LocationsBuilderMIPS64::VisitBooleanNot(HBooleanNot* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorMIPS64::VisitBooleanNot(HBooleanNot* instruction) { + LocationSummary* locations = instruction->GetLocations(); + __ Xori(locations->Out().AsRegister<GpuRegister>(), + locations->InAt(0).AsRegister<GpuRegister>(), + 1); +} + +void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (codegen_->CanMoveNullCheckToUser(instruction)) { + return; + } + Location obj = instruction->GetLocations()->InAt(0); + + __ Lw(ZERO, obj.AsRegister<GpuRegister>(), 0); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); +} + +void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instruction) { + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + + Location obj = instruction->GetLocations()->InAt(0); + + __ Beqzc(obj.AsRegister<GpuRegister>(), slow_path->GetEntryLabel()); +} + +void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) { + if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + GenerateImplicitNullCheck(instruction); + } else { + GenerateExplicitNullCheck(instruction); + } +} + +void LocationsBuilderMIPS64::VisitOr(HOr* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitOr(HOr* instruction) { + HandleBinaryOp(instruction); +} + +void LocationsBuilderMIPS64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { + LOG(FATAL) << "Unreachable"; +} + +void InstructionCodeGeneratorMIPS64::VisitParallelMove(HParallelMove* instruction) { + codegen_->GetMoveResolver()->EmitNativeCode(instruction); +} + +void LocationsBuilderMIPS64::VisitParameterValue(HParameterValue* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); + if (location.IsStackSlot()) { + location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } else if (location.IsDoubleStackSlot()) { + location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } + locations->SetOut(location); +} + +void InstructionCodeGeneratorMIPS64::VisitParameterValue(HParameterValue* instruction + ATTRIBUTE_UNUSED) { + // Nothing to do, the parameter is already at its location. +} + +void LocationsBuilderMIPS64::VisitCurrentMethod(HCurrentMethod* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); +} + +void InstructionCodeGeneratorMIPS64::VisitCurrentMethod(HCurrentMethod* instruction + ATTRIBUTE_UNUSED) { + // Nothing to do, the method is already at its location. +} + +void LocationsBuilderMIPS64::VisitPhi(HPhi* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { + locations->SetInAt(i, Location::Any()); + } + locations->SetOut(Location::Any()); +} + +void InstructionCodeGeneratorMIPS64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { + LOG(FATAL) << "Unreachable"; +} + +void LocationsBuilderMIPS64::VisitRem(HRem* rem) { + Primitive::Type type = rem->GetResultType(); + LocationSummary::CallKind call_kind = + Primitive::IsFloatingPointType(type) ? LocationSummary::kCall : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); + locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(type)); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + +void InstructionCodeGeneratorMIPS64::VisitRem(HRem* instruction) { + Primitive::Type type = instruction->GetType(); + LocationSummary* locations = instruction->GetLocations(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>(); + if (type == Primitive::kPrimInt) + __ ModR6(dst, lhs, rhs); + else + __ Dmod(dst, lhs, rhs); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + int32_t entry_offset = (type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pFmodf) + : QUICK_ENTRY_POINT(pFmod); + codegen_->InvokeRuntime(entry_offset, instruction, instruction->GetDexPc(), nullptr); + break; + } + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + +void LocationsBuilderMIPS64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { + memory_barrier->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { + GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); +} + +void LocationsBuilderMIPS64::VisitReturn(HReturn* ret) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret); + Primitive::Type return_type = ret->InputAt(0)->GetType(); + locations->SetInAt(0, Mips64ReturnLocation(return_type)); +} + +void InstructionCodeGeneratorMIPS64::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) { + codegen_->GenerateFrameExit(); +} + +void LocationsBuilderMIPS64::VisitReturnVoid(HReturnVoid* ret) { + ret->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) { + codegen_->GenerateFrameExit(); +} + +void LocationsBuilderMIPS64::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void InstructionCodeGeneratorMIPS64::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void LocationsBuilderMIPS64::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void InstructionCodeGeneratorMIPS64::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void LocationsBuilderMIPS64::VisitStoreLocal(HStoreLocal* store) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store); + Primitive::Type field_type = store->InputAt(1)->GetType(); + switch (field_type) { + case Primitive::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + default: + LOG(FATAL) << "Unimplemented local type " << field_type; + } +} + +void InstructionCodeGeneratorMIPS64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { +} + +void LocationsBuilderMIPS64::VisitSub(HSub* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitSub(HSub* instruction) { + HandleBinaryOp(instruction); +} + +void LocationsBuilderMIPS64::VisitStaticFieldGet(HStaticFieldGet* instruction) { + HandleFieldGet(instruction, instruction->GetFieldInfo()); +} + +void InstructionCodeGeneratorMIPS64::VisitStaticFieldGet(HStaticFieldGet* instruction) { + HandleFieldGet(instruction, instruction->GetFieldInfo()); +} + +void LocationsBuilderMIPS64::VisitStaticFieldSet(HStaticFieldSet* instruction) { + HandleFieldSet(instruction, instruction->GetFieldInfo()); +} + +void InstructionCodeGeneratorMIPS64::VisitStaticFieldSet(HStaticFieldSet* instruction) { + HandleFieldSet(instruction, instruction->GetFieldInfo()); +} + +void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) { + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); +} + +void InstructionCodeGeneratorMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) { + HBasicBlock* block = instruction->GetBlock(); + if (block->GetLoopInformation() != nullptr) { + DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); + // The back edge will generate the suspend check. + return; + } + if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { + // The goto will generate the suspend check. + return; + } + GenerateSuspendCheck(instruction, nullptr); +} + +void LocationsBuilderMIPS64::VisitTemporary(HTemporary* temp) { + temp->SetLocations(nullptr); +} + +void InstructionCodeGeneratorMIPS64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { + // Nothing to do, this is driven by the code generator. +} + +void LocationsBuilderMIPS64::VisitThrow(HThrow* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); +} + +void InstructionCodeGeneratorMIPS64::VisitThrow(HThrow* instruction) { + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pDeliverException), + instruction, + instruction->GetDexPc(), + nullptr); + CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); +} + +void LocationsBuilderMIPS64::VisitTypeConversion(HTypeConversion* conversion) { + Primitive::Type input_type = conversion->GetInputType(); + Primitive::Type result_type = conversion->GetResultType(); + DCHECK_NE(input_type, result_type); + + if ((input_type == Primitive::kPrimNot) || (input_type == Primitive::kPrimVoid) || + (result_type == Primitive::kPrimNot) || (result_type == Primitive::kPrimVoid)) { + LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type; + } + + LocationSummary::CallKind call_kind = LocationSummary::kNoCall; + if ((Primitive::IsFloatingPointType(result_type) && input_type == Primitive::kPrimLong) || + (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type))) { + call_kind = LocationSummary::kCall; + } + + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind); + + if (call_kind == LocationSummary::kNoCall) { + if (Primitive::IsFloatingPointType(input_type)) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + } + + if (Primitive::IsFloatingPointType(result_type)) { + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + } else { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } + } else { + InvokeRuntimeCallingConvention calling_convention; + + if (Primitive::IsFloatingPointType(input_type)) { + locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); + } else { + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + } + + locations->SetOut(calling_convention.GetReturnLocation(result_type)); + } +} + +void InstructionCodeGeneratorMIPS64::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = conversion->GetLocations(); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + + DCHECK_NE(input_type, result_type); + + if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>(); + + switch (result_type) { + case Primitive::kPrimChar: + __ Andi(dst, src, 0xFFFF); + break; + case Primitive::kPrimByte: + // long is never converted into types narrower than int directly, + // so SEB and SEH can be used without ever causing unpredictable results + // on 64-bit inputs + DCHECK(input_type != Primitive::kPrimLong); + __ Seb(dst, src); + break; + case Primitive::kPrimShort: + // long is never converted into types narrower than int directly, + // so SEB and SEH can be used without ever causing unpredictable results + // on 64-bit inputs + DCHECK(input_type != Primitive::kPrimLong); + __ Seh(dst, src); + break; + case Primitive::kPrimInt: + case Primitive::kPrimLong: + // Sign-extend 32-bit int into bits 32 through 63 for + // int-to-long and long-to-int conversions + __ Sll(dst, src, 0); + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + } else if (Primitive::IsFloatingPointType(result_type) && Primitive::IsIntegralType(input_type)) { + if (input_type != Primitive::kPrimLong) { + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + GpuRegister src = locations->InAt(0).AsRegister<GpuRegister>(); + __ Mtc1(src, FTMP); + if (result_type == Primitive::kPrimFloat) { + __ Cvtsw(dst, FTMP); + } else { + __ Cvtdw(dst, FTMP); + } + } else { + int32_t entry_offset = (result_type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pL2f) + : QUICK_ENTRY_POINT(pL2d); + codegen_->InvokeRuntime(entry_offset, + conversion, + conversion->GetDexPc(), + nullptr); + } + } else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) { + CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong); + int32_t entry_offset; + if (result_type != Primitive::kPrimLong) { + entry_offset = (input_type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pF2iz) + : QUICK_ENTRY_POINT(pD2iz); + } else { + entry_offset = (input_type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pF2l) + : QUICK_ENTRY_POINT(pD2l); + } + codegen_->InvokeRuntime(entry_offset, + conversion, + conversion->GetDexPc(), + nullptr); + } else if (Primitive::IsFloatingPointType(result_type) && + Primitive::IsFloatingPointType(input_type)) { + FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); + FpuRegister src = locations->InAt(0).AsFpuRegister<FpuRegister>(); + if (result_type == Primitive::kPrimFloat) { + __ Cvtsd(dst, src); + } else { + __ Cvtds(dst, src); + } + } else { + LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type + << " to " << result_type; + } +} + +void LocationsBuilderMIPS64::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + +void InstructionCodeGeneratorMIPS64::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + +void LocationsBuilderMIPS64::VisitXor(HXor* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitXor(HXor* instruction) { + HandleBinaryOp(instruction); +} + +void LocationsBuilderMIPS64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { + // Nothing to do, this should be removed during prepare for register allocator. + LOG(FATAL) << "Unreachable"; +} + +void InstructionCodeGeneratorMIPS64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { + // Nothing to do, this should be removed during prepare for register allocator. + LOG(FATAL) << "Unreachable"; +} + +void LocationsBuilderMIPS64::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitEqual(HEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitNotEqual(HNotEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitLessThan(HLessThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitLessThanOrEqual(HLessThanOrEqual* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitGreaterThan(HGreaterThan* comp) { + VisitCondition(comp); +} + +void LocationsBuilderMIPS64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); +} + +void InstructionCodeGeneratorMIPS64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { + VisitCondition(comp); +} + +} // namespace mips64 +} // namespace art diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h new file mode 100644 index 0000000000..ec36496a4f --- /dev/null +++ b/compiler/optimizing/code_generator_mips64.h @@ -0,0 +1,302 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_MIPS64_H_ +#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_MIPS64_H_ + +#include "code_generator.h" +#include "dex/compiler_enums.h" +#include "driver/compiler_options.h" +#include "nodes.h" +#include "parallel_move_resolver.h" +#include "utils/mips64/assembler_mips64.h" + +namespace art { +namespace mips64 { + +// Use a local definition to prevent copying mistakes. +static constexpr size_t kMips64WordSize = kMips64PointerSize; + + +// InvokeDexCallingConvention registers + +static constexpr GpuRegister kParameterCoreRegisters[] = + { A1, A2, A3, A4, A5, A6, A7 }; +static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); + +static constexpr FpuRegister kParameterFpuRegisters[] = + { F13, F14, F15, F16, F17, F18, F19 }; +static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters); + + +// InvokeRuntimeCallingConvention registers + +static constexpr GpuRegister kRuntimeParameterCoreRegisters[] = + { A0, A1, A2, A3, A4, A5, A6, A7 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +static constexpr FpuRegister kRuntimeParameterFpuRegisters[] = + { F12, F13, F14, F15, F16, F17, F18, F19 }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = + arraysize(kRuntimeParameterFpuRegisters); + + +static constexpr GpuRegister kCoreCalleeSaves[] = + { S0, S1, S2, S3, S4, S5, S6, S7, GP, S8, RA }; // TODO: review +static constexpr FpuRegister kFpuCalleeSaves[] = + { F24, F25, F26, F27, F28, F29, F30, F31 }; + + +class CodeGeneratorMIPS64; + +class InvokeDexCallingConvention : public CallingConvention<GpuRegister, FpuRegister> { + public: + InvokeDexCallingConvention() + : CallingConvention(kParameterCoreRegisters, + kParameterCoreRegistersLength, + kParameterFpuRegisters, + kParameterFpuRegistersLength, + kMips64PointerSize) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); +}; + +class InvokeDexCallingConventionVisitorMIPS64 : public InvokeDexCallingConventionVisitor { + public: + InvokeDexCallingConventionVisitorMIPS64() {} + virtual ~InvokeDexCallingConventionVisitorMIPS64() {} + + Location GetNextLocation(Primitive::Type type) OVERRIDE; + Location GetReturnLocation(Primitive::Type type) const OVERRIDE; + Location GetMethodLocation() const OVERRIDE; + + private: + InvokeDexCallingConvention calling_convention; + + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorMIPS64); +}; + +class InvokeRuntimeCallingConvention : public CallingConvention<GpuRegister, FpuRegister> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength, + kMips64PointerSize) {} + + Location GetReturnLocation(Primitive::Type return_type); + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + +class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap { + public: + ParallelMoveResolverMIPS64(ArenaAllocator* allocator, CodeGeneratorMIPS64* codegen) + : ParallelMoveResolverWithSwap(allocator), codegen_(codegen) {} + + void EmitMove(size_t index) OVERRIDE; + void EmitSwap(size_t index) OVERRIDE; + void SpillScratch(int reg) OVERRIDE; + void RestoreScratch(int reg) OVERRIDE; + + void Exchange(int index1, int index2, bool double_slot); + + Mips64Assembler* GetAssembler() const; + + private: + CodeGeneratorMIPS64* const codegen_; + + DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverMIPS64); +}; + +class SlowPathCodeMIPS64 : public SlowPathCode { + public: + SlowPathCodeMIPS64() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeMIPS64); +}; + +class LocationsBuilderMIPS64 : public HGraphVisitor { + public: + LocationsBuilderMIPS64(HGraph* graph, CodeGeneratorMIPS64* codegen) + : HGraphVisitor(graph), codegen_(codegen) {} + +#define DECLARE_VISIT_INSTRUCTION(name, super) \ + void Visit##name(H##name* instr); + + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + +#undef DECLARE_VISIT_INSTRUCTION + + private: + void HandleInvoke(HInvoke* invoke); + void HandleBinaryOp(HBinaryOperation* operation); + void HandleShift(HBinaryOperation* operation); + void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); + void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + + InvokeDexCallingConventionVisitorMIPS64 parameter_visitor_; + + CodeGeneratorMIPS64* const codegen_; + + DISALLOW_COPY_AND_ASSIGN(LocationsBuilderMIPS64); +}; + +class InstructionCodeGeneratorMIPS64 : public HGraphVisitor { + public: + InstructionCodeGeneratorMIPS64(HGraph* graph, CodeGeneratorMIPS64* codegen); + +#define DECLARE_VISIT_INSTRUCTION(name, super) \ + void Visit##name(H##name* instr); + + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + +#undef DECLARE_VISIT_INSTRUCTION + + Mips64Assembler* GetAssembler() const { return assembler_; } + + private: + // Generate code for the given suspend check. If not null, `successor` + // is the block to branch to if the suspend check is not needed, and after + // the suspend call. + void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg); + void GenerateMemoryBarrier(MemBarrierKind kind); + void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); + void HandleBinaryOp(HBinaryOperation* operation); + void HandleShift(HBinaryOperation* operation); + void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); + void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); + void GenerateTestAndBranch(HInstruction* instruction, + Label* true_target, + Label* false_target, + Label* always_true_target); + + Mips64Assembler* const assembler_; + CodeGeneratorMIPS64* const codegen_; + + DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorMIPS64); +}; + +class CodeGeneratorMIPS64 : public CodeGenerator { + public: + CodeGeneratorMIPS64(HGraph* graph, + const Mips64InstructionSetFeatures& isa_features, + const CompilerOptions& compiler_options); + virtual ~CodeGeneratorMIPS64() {} + + void GenerateFrameEntry() OVERRIDE; + void GenerateFrameExit() OVERRIDE; + + void Bind(HBasicBlock* block) OVERRIDE; + + void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; + + size_t GetWordSize() const OVERRIDE { return kMips64WordSize; } + + size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMips64WordSize; } + + uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + return GetLabelOf(block)->Position(); + } + + 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); + + // Register allocation. + + void SetupBlockedRegisters(bool is_baseline) const OVERRIDE; + // AllocateFreeRegister() is only used when allocating registers locally + // during CompileBaseline(). + Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; + + Location GetStackLocation(HLoadLocal* load) const OVERRIDE; + + size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id); + size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id); + size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id); + size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id); + + void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; + void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + + InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kMips64; } + + const Mips64InstructionSetFeatures& GetInstructionSetFeatures() const { + return isa_features_; + } + + Label* GetLabelOf(HBasicBlock* block) const { + return CommonGetLabelOf<Label>(block_labels_.GetRawStorage(), block); + } + + void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + + void Finalize(CodeAllocator* allocator) OVERRIDE; + + // Code generation helpers. + + void MoveLocation(Location destination, Location source, Primitive::Type type); + + void SwapLocations(Location loc1, Location loc2, Primitive::Type type); + + // Generate code to invoke a runtime entry point. + void InvokeRuntime(int32_t offset, + HInstruction* instruction, + uint32_t dex_pc, + SlowPathCode* slow_path); + + ParallelMoveResolver* GetMoveResolver() OVERRIDE { return &move_resolver_; } + + bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const { return false; } + + void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp); + + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; + Label frame_entry_label_; + LocationsBuilderMIPS64 location_builder_; + InstructionCodeGeneratorMIPS64 instruction_visitor_; + ParallelMoveResolverMIPS64 move_resolver_; + Mips64Assembler assembler_; + const Mips64InstructionSetFeatures& isa_features_; + + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS64); +}; + +} // namespace mips64 +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_MIPS64_H_ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index e39a1c2bd5..4d106c4fa4 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -56,6 +56,8 @@ class NullCheckSlowPathX86 : public SlowPathCodeX86 { RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); } + const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86"; } + private: HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); @@ -71,6 +73,8 @@ class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 { RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); } + const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86"; } + private: HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86); @@ -90,6 +94,8 @@ class DivRemMinusOneSlowPathX86 : public SlowPathCodeX86 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "DivRemMinusOneSlowPathX86"; } + private: Register reg_; bool is_div_; @@ -122,6 +128,8 @@ class BoundsCheckSlowPathX86 : public SlowPathCodeX86 { RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); } + const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86"; } + private: HBoundsCheck* const instruction_; const Location index_location_; @@ -158,6 +166,8 @@ class SuspendCheckSlowPathX86 : public SlowPathCodeX86 { return successor_; } + const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86"; } + private: HSuspendCheck* const instruction_; HBasicBlock* const successor_; @@ -188,6 +198,8 @@ class LoadStringSlowPathX86 : public SlowPathCodeX86 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86"; } + private: HLoadString* const instruction_; @@ -228,6 +240,8 @@ class LoadClassSlowPathX86 : public SlowPathCodeX86 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathX86"; } + private: // The class this slow path will load. HLoadClass* const cls_; @@ -293,6 +307,8 @@ class TypeCheckSlowPathX86 : public SlowPathCodeX86 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathX86"; } + private: HInstruction* const instruction_; const Location class_to_check_; @@ -318,6 +334,8 @@ class DeoptimizationSlowPathX86 : public SlowPathCodeX86 { codegen->RecordPcInfo(instruction_, dex_pc, this); } + const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86"; } + private: HInstruction* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 696d8d549e..1ad89c9bfc 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.cc b/compiler/optimizing/code_generator_x86_64.cc index 4da5bc4e51..a8f57cc9ac 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -59,6 +59,8 @@ class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 { RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); } + const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86_64"; } + private: HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); @@ -75,6 +77,8 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 { RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); } + const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86_64"; } + private: HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64); @@ -105,6 +109,8 @@ class DivRemMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "DivRemMinusOneSlowPathX86_64"; } + private: const CpuRegister cpu_reg_; const Primitive::Type type_; @@ -140,6 +146,8 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 { return successor_; } + const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86_64"; } + private: HSuspendCheck* const instruction_; HBasicBlock* const successor_; @@ -174,6 +182,8 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { RecordPcInfo(codegen, instruction_, instruction_->GetDexPc()); } + const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86_64"; } + private: HBoundsCheck* const instruction_; const Location index_location_; @@ -217,6 +227,8 @@ class LoadClassSlowPathX86_64 : public SlowPathCodeX86_64 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathX86_64"; } + private: // The class this slow path will load. HLoadClass* const cls_; @@ -257,6 +269,8 @@ class LoadStringSlowPathX86_64 : public SlowPathCodeX86_64 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86_64"; } + private: HLoadString* const instruction_; @@ -312,6 +326,8 @@ class TypeCheckSlowPathX86_64 : public SlowPathCodeX86_64 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathX86_64"; } + private: HInstruction* const instruction_; const Location class_to_check_; @@ -337,6 +353,8 @@ class DeoptimizationSlowPathX86_64 : public SlowPathCodeX86_64 { codegen->RecordPcInfo(instruction_, dex_pc, this); } + const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86_64"; } + private: HInstruction* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86_64); @@ -373,7 +391,7 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo if (invoke->IsStringInit()) { CpuRegister reg = temp.AsRegister<CpuRegister>(); // temp = thread->string_init_entrypoint - __ gs()->movl(reg, Address::Absolute(invoke->GetStringInitOffset())); + __ gs()->movq(reg, Address::Absolute(invoke->GetStringInitOffset(), true)); // (temp + offset_of_quick_compiled_code)() __ call(Address(reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset( kX86_64WordSize).SizeValue())); diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 215754cd46..a18e89a3e7 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/codegen_test.cc b/compiler/optimizing/codegen_test.cc index bfed1a89de..ca85cf5fae 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -20,6 +20,8 @@ #include "arch/arm/instruction_set_features_arm.h" #include "arch/arm/registers_arm.h" #include "arch/arm64/instruction_set_features_arm64.h" +#include "arch/mips64/instruction_set_features_mips64.h" +#include "arch/mips64/registers_mips64.h" #include "arch/x86/instruction_set_features_x86.h" #include "arch/x86/registers_x86.h" #include "arch/x86_64/instruction_set_features_x86_64.h" @@ -27,6 +29,7 @@ #include "builder.h" #include "code_generator_arm.h" #include "code_generator_arm64.h" +#include "code_generator_mips64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" #include "common_compiler_test.h" @@ -40,6 +43,7 @@ #include "ssa_liveness_analysis.h" #include "utils.h" #include "utils/arm/managed_register_arm.h" +#include "utils/mips64/managed_register_mips64.h" #include "utils/x86/managed_register_x86.h" #include "gtest/gtest.h" @@ -172,6 +176,14 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { if (kRuntimeISA == kArm64) { Run(allocator, codegenARM64, has_result, expected); } + + std::unique_ptr<const Mips64InstructionSetFeatures> features_mips64( + Mips64InstructionSetFeatures::FromCppDefines()); + mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options); + codegenMIPS64.CompileBaseline(&allocator, true); + if (kRuntimeISA == kMips64) { + Run(allocator, codegenMIPS64, has_result, expected); + } } template <typename Expected> @@ -219,6 +231,11 @@ static void RunCodeOptimized(HGraph* graph, X86_64InstructionSetFeatures::FromCppDefines()); x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options); RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected); + } else if (kRuntimeISA == kMips64) { + std::unique_ptr<const Mips64InstructionSetFeatures> features_mips64( + Mips64InstructionSetFeatures::FromCppDefines()); + mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options); + RunCodeOptimized(&codegenMIPS64, graph, hook_before_codegen, has_result, expected); } } diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 422223f5e0..11f6362294 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -627,8 +627,8 @@ TEST(ConstantFolding, ConstantCondition) { " 9: If(8)\n" "BasicBlock 2, pred: 1, succ: 3\n" " 12: Goto 3\n" - "BasicBlock 3, pred: 2, 5, succ: 4\n" - " 22: Phi(3, 5) [15]\n" + "BasicBlock 3, pred: 5, 2, succ: 4\n" + " 22: Phi(5, 3) [15]\n" " 15: Add(22, 3)\n" " 17: ReturnVoid\n" "BasicBlock 4, pred: 3\n" diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 3209d3eb18..ee3a61aa0c 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -89,8 +89,8 @@ TEST(DeadCodeElimination, AdditionAndConditionalJump) { " 9: If(8)\n" "BasicBlock 2, pred: 1, succ: 3\n" " 12: Goto 3\n" - "BasicBlock 3, pred: 2, 5, succ: 4\n" - " 22: Phi(3, 5) [15]\n" + "BasicBlock 3, pred: 5, 2, succ: 4\n" + " 22: Phi(5, 3) [15]\n" " 15: Add(22, 3)\n" " 17: ReturnVoid\n" "BasicBlock 4, pred: 3\n" @@ -101,7 +101,7 @@ TEST(DeadCodeElimination, AdditionAndConditionalJump) { // Expected difference after dead code elimination. diff_t expected_diff = { { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [22, 8]\n" }, - { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, + { " 22: Phi(5, 3) [15]\n", " 22: Phi(5, 3)\n" }, { " 15: Add(22, 3)\n", removed } }; std::string expected_after = Patch(expected_before, expected_diff); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index b64791788d..7d723ef13d 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -16,17 +16,21 @@ #include "graph_visualizer.h" +#include <dlfcn.h> + +#include <cctype> +#include <sstream> + #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 <cctype> -#include <sstream> +#include "utils/assembler.h" namespace art { @@ -87,6 +91,64 @@ 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), disassembler_(nullptr) { + 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<create_disasm_prototype*>( + 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<Disassembler>((*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 { + if (disassembler_ == nullptr) { + return; + } + + 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> disassembler_; + + void* libart_disassembler_handle_; +}; + + /** * HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra. */ @@ -96,12 +158,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 +242,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 +255,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 +343,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 { @@ -359,9 +436,13 @@ class HGraphVisualizerPrinter : public HGraphVisitor { && is_after_pass_) { if (instruction->GetType() == Primitive::kPrimNot) { if (instruction->IsLoadClass()) { + ReferenceTypeInfo info = instruction->AsLoadClass()->GetLoadedClassRTI(); ScopedObjectAccess soa(Thread::Current()); - StartAttributeStream("klass") - << PrettyClass(instruction->AsLoadClass()->GetLoadedClassRTI().GetTypeHandle().Get()); + if (info.GetTypeHandle().GetReference() != nullptr) { + StartAttributeStream("klass") << info.GetTypeHandle().Get(); + } else { + StartAttributeStream("klass") << "unresolved"; + } } else { ReferenceTypeInfo info = instruction->GetReferenceTypeInfo(); if (info.IsTop()) { @@ -374,10 +455,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; @@ -395,11 +486,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"); } @@ -446,11 +609,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<HGraphVisualizerDisassembler> disassembler_; size_t indent_; DISALLOW_COPY_AND_ASSIGN(HGraphVisualizerPrinter); @@ -479,4 +648,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 513bceb369..b6b66df601 100644 --- a/compiler/optimizing/graph_visualizer.h +++ b/compiler/optimizing/graph_visualizer.h @@ -19,6 +19,8 @@ #include <ostream> +#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<const HInstruction*>(), 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<const HInstruction*, GeneratedCodeInterval>& GetInstructionIntervals() const { + return instruction_intervals_; + } + + ArenaSafeMap<const HInstruction*, GeneratedCodeInterval>* GetInstructionIntervals() { + return &instruction_intervals_; + } + + const ArenaVector<SlowPathCodeInfo>& GetSlowPathIntervals() const { return slow_path_intervals_; } + + ArenaVector<SlowPathCodeInfo>* GetSlowPathIntervals() { return &slow_path_intervals_; } + + private: + GeneratedCodeInterval frame_entry_interval_; + ArenaSafeMap<const HInstruction*, GeneratedCodeInterval> instruction_intervals_; + ArenaVector<SlowPathCodeInfo> 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/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2daeeb3c0c..e375f7b1d3 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -312,17 +312,23 @@ void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) { HInstruction* input_value = equal->GetLeastConstantLeft(); if (input_value->GetType() == Primitive::kPrimBoolean && input_const->IsIntConstant()) { HBasicBlock* block = equal->GetBlock(); + // We are comparing the boolean to a constant which is of type int and can + // be any constant. if (input_const->AsIntConstant()->IsOne()) { // Replace (bool_value == true) with bool_value equal->ReplaceWith(input_value); block->RemoveInstruction(equal); RecordSimplification(); - } else { + } else if (input_const->AsIntConstant()->IsZero()) { // Replace (bool_value == false) with !bool_value - DCHECK(input_const->AsIntConstant()->IsZero()); block->ReplaceAndRemoveInstructionWith( equal, new (block->GetGraph()->GetArena()) HBooleanNot(input_value)); RecordSimplification(); + } else { + // Replace (bool_value == integer_not_zero_nor_one_constant) with false + equal->ReplaceWith(GetGraph()->GetIntConstant(0)); + block->RemoveInstruction(equal); + RecordSimplification(); } } } @@ -334,17 +340,23 @@ void InstructionSimplifierVisitor::VisitNotEqual(HNotEqual* not_equal) { HInstruction* input_value = not_equal->GetLeastConstantLeft(); if (input_value->GetType() == Primitive::kPrimBoolean && input_const->IsIntConstant()) { HBasicBlock* block = not_equal->GetBlock(); + // We are comparing the boolean to a constant which is of type int and can + // be any constant. if (input_const->AsIntConstant()->IsOne()) { // Replace (bool_value != true) with !bool_value block->ReplaceAndRemoveInstructionWith( not_equal, new (block->GetGraph()->GetArena()) HBooleanNot(input_value)); RecordSimplification(); - } else { + } else if (input_const->AsIntConstant()->IsZero()) { // Replace (bool_value != false) with bool_value - DCHECK(input_const->AsIntConstant()->IsZero()); not_equal->ReplaceWith(input_value); block->RemoveInstruction(not_equal); RecordSimplification(); + } else { + // Replace (bool_value != integer_not_zero_nor_one_constant) with true + not_equal->ReplaceWith(GetGraph()->GetIntConstant(1)); + block->RemoveInstruction(not_equal); + RecordSimplification(); } } } diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 749bedf99e..71fadfbcc2 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -121,6 +121,8 @@ class IntrinsicSlowPathARM : public SlowPathCodeARM { __ b(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM"; } + private: // The instruction where this slow path is happening. HInvoke* const invoke_; diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index c108ad5daa..8bcb88b4ea 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -130,6 +130,8 @@ class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 { __ B(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathARM64"; } + private: // The instruction where this slow path is happening. HInvoke* const invoke_; diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 424ac7c855..b04cc5cace 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -158,6 +158,8 @@ class IntrinsicSlowPathX86 : public SlowPathCodeX86 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathX86"; } + private: // The instruction where this slow path is happening. HInvoke* const invoke_; diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 891531435e..888c7b8037 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -149,6 +149,8 @@ class IntrinsicSlowPathX86_64 : public SlowPathCodeX86_64 { __ jmp(GetExitLabel()); } + const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathX86_64"; } + private: // The instruction where this slow path is happening. HInvoke* const invoke_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 68c197e607..01eb2d7f86 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -180,8 +180,9 @@ void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) { HBasicBlock* new_block = new (arena_) HBasicBlock(this, successor->GetDexPc()); AddBlock(new_block); new_block->AddInstruction(new (arena_) HGoto()); - block->ReplaceSuccessor(successor, new_block); - new_block->AddSuccessor(successor); + // Use `InsertBetween` to ensure the predecessor index and successor index of + // `block` and `successor` are preserved. + new_block->InsertBetween(block, successor); if (successor->IsLoopHeader()) { // If we split at a back edge boundary, make the new block the back edge. HLoopInformation* info = successor->GetLoopInformation(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9443653db7..26eee1c52e 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -625,6 +625,20 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { predecessors_.Put(predecessor_index, new_block); } + // Insert `this` between `predecessor` and `successor. This method + // preserves the indicies, and will update the first edge found between + // `predecessor` and `successor`. + void InsertBetween(HBasicBlock* predecessor, HBasicBlock* successor) { + size_t predecessor_index = successor->GetPredecessorIndexOf(predecessor); + DCHECK_NE(predecessor_index, static_cast<size_t>(-1)); + size_t successor_index = predecessor->GetSuccessorIndexOf(successor); + DCHECK_NE(successor_index, static_cast<size_t>(-1)); + successor->predecessors_.Put(predecessor_index, this); + predecessor->successors_.Put(successor_index, this); + successors_.Add(successor); + predecessors_.Add(predecessor); + } + void RemovePredecessor(HBasicBlock* block) { predecessors_.Delete(block); } @@ -2190,8 +2204,12 @@ class HCompare : public HBinaryOperation { kLtBias, // return -1 for NaN comparisons }; - HCompare(Primitive::Type type, HInstruction* first, HInstruction* second, Bias bias) - : HBinaryOperation(Primitive::kPrimInt, first, second), bias_(bias) { + HCompare(Primitive::Type type, + HInstruction* first, + HInstruction* second, + Bias bias, + uint32_t dex_pc) + : HBinaryOperation(Primitive::kPrimInt, first, second), bias_(bias), dex_pc_(dex_pc) { DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } @@ -2216,10 +2234,13 @@ class HCompare : public HBinaryOperation { bool IsGtBias() { return bias_ == kGtBias; } + uint32_t GetDexPc() const { return dex_pc_; } + DECLARE_INSTRUCTION(Compare); private: const Bias bias_; + const uint32_t dex_pc_; DISALLOW_COPY_AND_ASSIGN(HCompare); }; @@ -4027,6 +4048,8 @@ class MoveOperands : public ArenaObject<kArenaAllocMisc> { return source_.IsInvalid(); } + Primitive::Type GetType() const { return type_; } + bool Is64BitMove() const { return Primitive::Is64BitType(type_); } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 8d43adaada..0c7b6f7093 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<OptimizingCompilerStats> compilation_stats_; @@ -290,6 +301,7 @@ bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED, static bool IsInstructionSetSupported(InstructionSet instruction_set) { return instruction_set == kArm64 || (instruction_set == kThumb2 && !kArm32QuickCodeUseSoftFloat) + || instruction_set == kMips64 || instruction_set == kX86 || instruction_set == kX86_64; } @@ -428,7 +440,7 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, MaybeRecordStat(MethodCompilationStat::kCompiledOptimized); - return CompiledMethod::SwapAllocCompiledMethod( + CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( compiler_driver, codegen->GetInstructionSet(), ArrayRef<const uint8_t>(allocator.GetMemory()), @@ -444,12 +456,15 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, ArrayRef<const uint8_t>(), // native_gc_map. ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()), ArrayRef<const LinkerPatch>()); + 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); @@ -465,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<const uint8_t>(allocator.GetMemory()), @@ -481,6 +496,8 @@ CompiledMethod* OptimizingCompiler::CompileBaseline( AlignVectorSize(gc_map), ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()), ArrayRef<const LinkerPatch>()); + pass_info_printer->DumpDisassembly(); + return compiled_method; } CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item, @@ -556,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); @@ -616,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/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index a048c856c5..67840984b1 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -222,9 +222,10 @@ void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr, ClassLinker* cl = Runtime::Current()->GetClassLinker(); mirror::DexCache* dex_cache = cl->FindDexCache(info.GetDexFile()); ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache); - DCHECK(field != nullptr); - mirror::Class* klass = field->GetType<false>(); - SetClassAsTypeInfo(instr, klass, /* is_exact */ false); + if (field != nullptr) { + mirror::Class* klass = field->GetType<false>(); + SetClassAsTypeInfo(instr, klass, /* is_exact */ false); + } } void RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) { @@ -323,9 +324,10 @@ void RTPVisitor::VisitInvoke(HInvoke* instr) { mirror::DexCache* dex_cache = cl->FindDexCache(instr->GetDexFile()); ArtMethod* method = dex_cache->GetResolvedMethod( instr->GetDexMethodIndex(), cl->GetImagePointerSize()); - DCHECK(method != nullptr); - mirror::Class* klass = method->GetReturnType(false); - SetClassAsTypeInfo(instr, klass, /* is_exact */ false); + if (method != nullptr) { + mirror::Class* klass = method->GetReturnType(false); + SetClassAsTypeInfo(instr, klass, /* is_exact */ false); + } } void RTPVisitor::VisitArrayGet(HArrayGet* instr) { diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index e38e49cd19..7b23d020c2 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -80,6 +80,7 @@ bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph ATTRIBUTE_UN InstructionSet instruction_set) { return instruction_set == kArm64 || instruction_set == kX86_64 + || instruction_set == kMips64 || instruction_set == kArm || instruction_set == kX86 || instruction_set == kThumb2; diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index fb3e7d798c..0e8c058002 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -115,7 +115,7 @@ TEST(SsaTest, CFG1) { " 3: If(2)\n" "BasicBlock 2, pred: 1, succ: 3\n" " 4: Goto\n" - "BasicBlock 3, pred: 2, 5, succ: 4\n" + "BasicBlock 3, pred: 5, 2, succ: 4\n" " 5: ReturnVoid\n" "BasicBlock 4, pred: 3\n" " 6: Exit\n" @@ -145,8 +145,8 @@ TEST(SsaTest, CFG2) { " 4: If(3)\n" "BasicBlock 2, pred: 1, succ: 3\n" " 5: Goto\n" - "BasicBlock 3, pred: 2, 5, succ: 4\n" - " 6: Phi(1, 0) [7]\n" + "BasicBlock 3, pred: 5, 2, succ: 4\n" + " 6: Phi(0, 1) [7]\n" " 7: Return(6)\n" "BasicBlock 4, pred: 3\n" " 8: Exit\n" @@ -428,8 +428,8 @@ TEST(SsaTest, Loop7) { " 10: Goto\n" "BasicBlock 5, pred: 3, succ: 2\n" " 11: Goto\n" - "BasicBlock 6, pred: 4, 8, succ: 7\n" - " 12: Phi(2, 5) [13]\n" + "BasicBlock 6, pred: 8, 4, succ: 7\n" + " 12: Phi(5, 2) [13]\n" " 13: Return(12)\n" "BasicBlock 7, pred: 6\n" " 14: Exit\n" @@ -480,7 +480,7 @@ TEST(SsaTest, LocalInIf) { " 4: If(3)\n" "BasicBlock 2, pred: 1, succ: 3\n" " 5: Goto\n" - "BasicBlock 3, pred: 2, 5, succ: 4\n" + "BasicBlock 3, pred: 5, 2, succ: 4\n" " 6: ReturnVoid\n" "BasicBlock 4, pred: 3\n" " 7: Exit\n" @@ -517,7 +517,7 @@ TEST(SsaTest, MultiplePredecessors) { " 8: Add(0, 0)\n" " 9: Goto\n" // This block should not get a phi for local 1. - "BasicBlock 5, pred: 2, 4, 7, succ: 6\n" + "BasicBlock 5, pred: 2, 7, 4, succ: 6\n" " 10: ReturnVoid\n" "BasicBlock 6, pred: 5\n" " 11: Exit\n" |