diff options
| -rw-r--r-- | compiler/Android.mk | 1 | ||||
| -rw-r--r-- | compiler/optimizing/builder.cc | 15 | ||||
| -rw-r--r-- | compiler/optimizing/builder.h | 5 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 18 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 3250 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.h | 302 | ||||
| -rw-r--r-- | compiler/optimizing/codegen_test.cc | 17 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 13 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/register_allocator.cc | 1 | ||||
| -rw-r--r-- | compiler/utils/mips64/assembler_mips64.cc | 894 | ||||
| -rw-r--r-- | compiler/utils/mips64/assembler_mips64.h | 150 | ||||
| -rw-r--r-- | compiler/utils/mips64/constants_mips64.h | 2 | ||||
| -rw-r--r-- | disassembler/disassembler_mips.cc | 136 | ||||
| -rw-r--r-- | disassembler/disassembler_mips.h | 9 | ||||
| -rw-r--r-- | test/Android.run-test.mk | 31 | 
16 files changed, 4589 insertions, 256 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 3f5271d31f..5770edfd58 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -103,6 +103,7 @@ LIBART_COMPILER_SRC_FILES := \  	optimizing/code_generator.cc \  	optimizing/code_generator_arm.cc \  	optimizing/code_generator_arm64.cc \ +	optimizing/code_generator_mips64.cc \  	optimizing/code_generator_x86.cc \  	optimizing/code_generator_x86_64.cc \  	optimizing/code_generator_utils.cc \ diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index b564aca27a..d175efe972 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());  } @@ -2072,27 +2073,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 b1ee824845..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); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 508e770494..ff047241e3 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -20,6 +20,7 @@  #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" @@ -459,6 +460,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(), @@ -629,18 +635,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_mips64.cc b/compiler/optimizing/code_generator_mips64.cc new file mode 100644 index 0000000000..69a90adafb --- /dev/null +++ b/compiler/optimizing/code_generator_mips64.cc @@ -0,0 +1,3250 @@ +/* + * 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::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>(); +  } + + 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>(); +  } + + 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()); +  } + + 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()); +  } + + 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>(); +  } + + 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_; +  } + + 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()); +  } + + 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); +  } + + 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(); +  if (locations != nullptr && locations->Out().Equals(location)) { +    return; +  } + +  Primitive::Type type = instruction->GetType(); +  DCHECK_NE(type, Primitive::kPrimVoid); + +  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::LoadCurrentMethod(GpuRegister current_method) { +  DCHECK(RequiresCurrentMethod()); +  __ Ld(current_method, SP, kCurrentMethodStackOffset); +} + +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) { +  LocationSummary* locations = +      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); +  locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); + +  InvokeDexCallingConventionVisitorMIPS64 calling_convention_visitor; +  for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { +    HInstruction* input = invoke->InputAt(i); +    locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); +  } + +  Primitive::Type return_type = invoke->GetType(); +  if (return_type != Primitive::kPrimVoid) { +    locations->SetOut(calling_convention_visitor.GetReturnLocation(return_type)); +  } +} + +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); +} + +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, +                                                     GpuRegister 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()) { +    // temp = thread->string_init_entrypoint +    __ LoadFromOffset(kLoadDoubleword, +                      temp, +                      TR, +                      invoke->GetStringInitOffset()); +    // T9 = temp->entry_point_from_quick_compiled_code_; +    __ LoadFromOffset(kLoadDoubleword, +                      T9, +                      temp, +                      ArtMethod::EntryPointFromQuickCompiledCodeOffset( +                          kMips64WordSize).Int32Value()); +    // T9() +    __ Jalr(T9); +  } else { +    // temp = method; +    LoadCurrentMethod(temp); +    if (!invoke->IsRecursive()) { +      // temp = temp->dex_cache_resolved_methods_; +      __ LoadFromOffset(kLoadUnsignedWord, +                        temp, +                        temp, +                        ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); +      // temp = temp[index_in_cache] +      __ LoadFromOffset(kLoadDoubleword, +                        temp, +                        temp, +                        CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())); +      // T9 = temp[offset_of_quick_compiled_code] +      __ LoadFromOffset(kLoadDoubleword, +                        T9, +                        temp, +                        ArtMethod::EntryPointFromQuickCompiledCodeOffset( +                            kMips64WordSize).Int32Value()); +      // T9() +      __ Jalr(T9); +    } else { +      __ Jalr(&frame_entry_label_, 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; +  } + +  GpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<GpuRegister>(); + +  codegen_->GenerateStaticOrDirectCall(invoke, temp); +  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(); +  if (receiver.IsStackSlot()) { +    __ LoadFromOffset(kLoadUnsignedWord, temp, SP, receiver.GetStackIndex()); +    __ LoadFromOffset(kLoadUnsignedWord, temp, temp, class_offset); +  } else { +    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->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { +  GpuRegister out = cls->GetLocations()->Out().AsRegister<GpuRegister>(); +  if (cls->IsReferrersClass()) { +    DCHECK(!cls->CanCallRuntime()); +    DCHECK(!cls->MustGenerateClinitCheck()); +    codegen_->LoadCurrentMethod(out); +    __ LoadFromOffset( +        kLoadUnsignedWord, out, out, ArtMethod::DeclaringClassOffset().Int32Value()); +  } else { +    DCHECK(cls->CanCallRuntime()); +    codegen_->LoadCurrentMethod(out); +    __ LoadFromOffset( +        kLoadUnsignedWord, out, out, 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->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) { +  SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load); +  codegen_->AddSlowPath(slow_path); + +  GpuRegister out = load->GetLocations()->Out().AsRegister<GpuRegister>(); +  codegen_->LoadCurrentMethod(out); +  __ LoadFromOffset( +      kLoadUnsignedWord, out, out, 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->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); +} + +void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) { +  InvokeRuntimeCallingConvention calling_convention; +  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2)); +  // Move an uint16_t value to a register. +  __ LoadConst32(calling_convention.GetRegisterAt(0), 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->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); +  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void InstructionCodeGeneratorMIPS64::VisitNewInstance(HNewInstance* instruction) { +  InvokeRuntimeCallingConvention calling_convention; +  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1)); +  // Move an uint16_t value to a register. +  __ LoadConst32(calling_convention.GetRegisterAt(0), 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::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..0ce0addee0 --- /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; + + 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_; } + +  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); + +  void LoadCurrentMethod(GpuRegister current_method); + +  // 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, GpuRegister 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/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 4db3b43abf..4fbb51d43c 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> @@ -222,6 +234,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/nodes.h b/compiler/optimizing/nodes.h index b36d9b8d2d..01ba1101f2 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2109,8 +2109,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());    } @@ -2135,10 +2139,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);  }; @@ -3862,6 +3869,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 58647415d7..75217c883e 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -291,6 +291,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;  } diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 5f439c86d9..8656ad5f8c 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/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 5e49b936f2..3333cd22bd 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -25,9 +25,9 @@  namespace art {  namespace mips64 { -void Mips64Assembler::Emit(int32_t value) { +void Mips64Assembler::Emit(uint32_t value) {    AssemblerBuffer::EnsureCapacity ensured(&buffer_); -  buffer_.Emit<int32_t>(value); +  buffer_.Emit<uint32_t>(value);  }  void Mips64Assembler::EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegister rd, @@ -35,124 +35,62 @@ void Mips64Assembler::EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegis    CHECK_NE(rs, kNoGpuRegister);    CHECK_NE(rt, kNoGpuRegister);    CHECK_NE(rd, kNoGpuRegister); -  int32_t encoding = opcode << kOpcodeShift | -                     static_cast<int32_t>(rs) << kRsShift | -                     static_cast<int32_t>(rt) << kRtShift | -                     static_cast<int32_t>(rd) << kRdShift | -                     shamt << kShamtShift | -                     funct; +  uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | +                      static_cast<uint32_t>(rs) << kRsShift | +                      static_cast<uint32_t>(rt) << kRtShift | +                      static_cast<uint32_t>(rd) << kRdShift | +                      shamt << kShamtShift | +                      funct;    Emit(encoding);  }  void Mips64Assembler::EmitI(int opcode, GpuRegister rs, GpuRegister rt, uint16_t imm) {    CHECK_NE(rs, kNoGpuRegister);    CHECK_NE(rt, kNoGpuRegister); -  int32_t encoding = opcode << kOpcodeShift | -                     static_cast<int32_t>(rs) << kRsShift | -                     static_cast<int32_t>(rt) << kRtShift | -                     imm; +  uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | +                      static_cast<uint32_t>(rs) << kRsShift | +                      static_cast<uint32_t>(rt) << kRtShift | +                      imm;    Emit(encoding);  } -void Mips64Assembler::EmitJ(int opcode, int address) { -  int32_t encoding = opcode << kOpcodeShift | -                     address; +void Mips64Assembler::EmitI21(int opcode, GpuRegister rs, uint32_t imm21) { +  CHECK_NE(rs, kNoGpuRegister); +  uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | +                      static_cast<uint32_t>(rs) << kRsShift | +                      (imm21 & 0x1FFFFF); +  Emit(encoding); +} + +void Mips64Assembler::EmitJ(int opcode, uint32_t addr26) { +  uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | +                      (addr26 & 0x3FFFFFF);    Emit(encoding);  }  void Mips64Assembler::EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd, -int funct) { +                             int funct) {    CHECK_NE(ft, kNoFpuRegister);    CHECK_NE(fs, kNoFpuRegister);    CHECK_NE(fd, kNoFpuRegister); -  int32_t encoding = opcode << kOpcodeShift | -                     fmt << kFmtShift | -                     static_cast<int32_t>(ft) << kFtShift | -                     static_cast<int32_t>(fs) << kFsShift | -                     static_cast<int32_t>(fd) << kFdShift | -                     funct; +  uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | +                      fmt << kFmtShift | +                      static_cast<uint32_t>(ft) << kFtShift | +                      static_cast<uint32_t>(fs) << kFsShift | +                      static_cast<uint32_t>(fd) << kFdShift | +                      funct;    Emit(encoding);  } -void Mips64Assembler::EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm) { -  CHECK_NE(rt, kNoFpuRegister); -  int32_t encoding = opcode << kOpcodeShift | -                     fmt << kFmtShift | -                     static_cast<int32_t>(rt) << kRtShift | -                     imm; +void Mips64Assembler::EmitFI(int opcode, int fmt, FpuRegister ft, uint16_t imm) { +  CHECK_NE(ft, kNoFpuRegister); +  uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | +                      fmt << kFmtShift | +                      static_cast<uint32_t>(ft) << kFtShift | +                      imm;    Emit(encoding);  } -void Mips64Assembler::EmitBranch(GpuRegister rt, GpuRegister rs, Label* label, bool equal) { -  int offset; -  if (label->IsBound()) { -    offset = label->Position() - buffer_.Size(); -  } else { -    // Use the offset field of the branch instruction for linking the sites. -    offset = label->position_; -    label->LinkTo(buffer_.Size()); -  } -  if (equal) { -    Beq(rt, rs, (offset >> 2) & kBranchOffsetMask); -  } else { -    Bne(rt, rs, (offset >> 2) & kBranchOffsetMask); -  } -} - -void Mips64Assembler::EmitJump(Label* label, bool link) { -  int offset; -  if (label->IsBound()) { -    offset = label->Position() - buffer_.Size(); -  } else { -    // Use the offset field of the jump instruction for linking the sites. -    offset = label->position_; -    label->LinkTo(buffer_.Size()); -  } -  if (link) { -    Jal((offset >> 2) & kJumpOffsetMask); -  } else { -    J((offset >> 2) & kJumpOffsetMask); -  } -} - -int32_t Mips64Assembler::EncodeBranchOffset(int offset, int32_t inst, bool is_jump) { -  CHECK_ALIGNED(offset, 4); -  CHECK(IsInt<POPCOUNT(kBranchOffsetMask)>(offset)) << offset; - -  // Properly preserve only the bits supported in the instruction. -  offset >>= 2; -  if (is_jump) { -    offset &= kJumpOffsetMask; -    return (inst & ~kJumpOffsetMask) | offset; -  } else { -    offset &= kBranchOffsetMask; -    return (inst & ~kBranchOffsetMask) | offset; -  } -} - -int Mips64Assembler::DecodeBranchOffset(int32_t inst, bool is_jump) { -  // Sign-extend, then left-shift by 2. -  if (is_jump) { -    return (((inst & kJumpOffsetMask) << 6) >> 4); -  } else { -    return (((inst & kBranchOffsetMask) << 16) >> 14); -  } -} - -void Mips64Assembler::Bind(Label* label, bool is_jump) { -  CHECK(!label->IsBound()); -  int bound_pc = buffer_.Size(); -  while (label->IsLinked()) { -    int32_t position = label->Position(); -    int32_t next = buffer_.Load<int32_t>(position); -    int32_t offset = is_jump ? bound_pc - position : bound_pc - position - 4; -    int32_t encoded = Mips64Assembler::EncodeBranchOffset(offset, next, is_jump); -    buffer_.Store<int32_t>(position, encoded); -    label->position_ = Mips64Assembler::DecodeBranchOffset(next, is_jump); -  } -  label->BindTo(bound_pc); -} -  void Mips64Assembler::Add(GpuRegister rd, GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, rd, 0, 0x20);  } @@ -169,6 +107,10 @@ void Mips64Assembler::Addiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {    EmitI(0x9, rs, rt, imm16);  } +void Mips64Assembler::Daddu(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 0, 0x2d); +} +  void Mips64Assembler::Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {    EmitI(0x19, rs, rt, imm16);  } @@ -181,22 +123,90 @@ void Mips64Assembler::Subu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, rd, 0, 0x23);  } -void Mips64Assembler::Mult(GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::Dsubu(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 0, 0x2f); +} + +void Mips64Assembler::MultR2(GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x18);  } -void Mips64Assembler::Multu(GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::MultuR2(GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x19);  } -void Mips64Assembler::Div(GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::DivR2(GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x1a);  } -void Mips64Assembler::Divu(GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::DivuR2(GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x1b);  } +void Mips64Assembler::MulR2(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0x1c, rs, rt, rd, 0, 2); +} + +void Mips64Assembler::DivR2(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  DivR2(rs, rt); +  Mflo(rd); +} + +void Mips64Assembler::ModR2(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  DivR2(rs, rt); +  Mfhi(rd); +} + +void Mips64Assembler::DivuR2(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  DivuR2(rs, rt); +  Mflo(rd); +} + +void Mips64Assembler::ModuR2(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  DivuR2(rs, rt); +  Mfhi(rd); +} + +void Mips64Assembler::MulR6(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 2, 0x18); +} + +void Mips64Assembler::DivR6(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 2, 0x1a); +} + +void Mips64Assembler::ModR6(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 3, 0x1a); +} + +void Mips64Assembler::DivuR6(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 2, 0x1b); +} + +void Mips64Assembler::ModuR6(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 3, 0x1b); +} + +void Mips64Assembler::Dmul(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 2, 0x1c); +} + +void Mips64Assembler::Ddiv(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 2, 0x1e); +} + +void Mips64Assembler::Dmod(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 3, 0x1e); +} + +void Mips64Assembler::Ddivu(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 2, 0x1f); +} + +void Mips64Assembler::Dmodu(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +  EmitR(0, rs, rt, rd, 3, 0x1f); +} +  void Mips64Assembler::And(GpuRegister rd, GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, rd, 0, 0x24);  } @@ -225,30 +235,80 @@ void Mips64Assembler::Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt) {    EmitR(0, rs, rt, rd, 0, 0x27);  } -void Mips64Assembler::Sll(GpuRegister rd, GpuRegister rs, int shamt) { -  EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x00); +void Mips64Assembler::Seb(GpuRegister rd, GpuRegister rt) { +  EmitR(0x1f, static_cast<GpuRegister>(0), rt, rd, 0x10, 0x20);  } -void Mips64Assembler::Srl(GpuRegister rd, GpuRegister rs, int shamt) { -  EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x02); +void Mips64Assembler::Seh(GpuRegister rd, GpuRegister rt) { +  EmitR(0x1f, static_cast<GpuRegister>(0), rt, rd, 0x18, 0x20);  } -void Mips64Assembler::Sra(GpuRegister rd, GpuRegister rs, int shamt) { -  EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x03); +void Mips64Assembler::Dext(GpuRegister rt, GpuRegister rs, int pos, int size_less_one) { +  DCHECK(0 <= pos && pos < 32) << pos; +  DCHECK(0 <= size_less_one && size_less_one < 32) << size_less_one; +  EmitR(0x1f, rs, rt, static_cast<GpuRegister>(size_less_one), pos, 3);  } -void Mips64Assembler::Sllv(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::Sll(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x00); +} + +void Mips64Assembler::Srl(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x02); +} + +void Mips64Assembler::Sra(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x03); +} + +void Mips64Assembler::Sllv(GpuRegister rd, GpuRegister rt, GpuRegister rs) {    EmitR(0, rs, rt, rd, 0, 0x04);  } -void Mips64Assembler::Srlv(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::Srlv(GpuRegister rd, GpuRegister rt, GpuRegister rs) {    EmitR(0, rs, rt, rd, 0, 0x06);  } -void Mips64Assembler::Srav(GpuRegister rd, GpuRegister rs, GpuRegister rt) { +void Mips64Assembler::Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs) {    EmitR(0, rs, rt, rd, 0, 0x07);  } +void Mips64Assembler::Dsll(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x38); +} + +void Mips64Assembler::Dsrl(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3a); +} + +void Mips64Assembler::Dsra(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3b); +} + +void Mips64Assembler::Dsll32(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3c); +} + +void Mips64Assembler::Dsrl32(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3e); +} + +void Mips64Assembler::Dsra32(GpuRegister rd, GpuRegister rt, int shamt) { +  EmitR(0, static_cast<GpuRegister>(0), rt, rd, shamt, 0x3f); +} + +void Mips64Assembler::Dsllv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { +  EmitR(0, rs, rt, rd, 0, 0x14); +} + +void Mips64Assembler::Dsrlv(GpuRegister rd, GpuRegister rt, GpuRegister rs) { +  EmitR(0, rs, rt, rd, 0, 0x16); +} + +void Mips64Assembler::Dsrav(GpuRegister rd, GpuRegister rt, GpuRegister rs) { +  EmitR(0, rs, rt, rd, 0, 0x17); +} +  void Mips64Assembler::Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16) {    EmitI(0x20, rs, rt, imm16);  } @@ -281,6 +341,19 @@ void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) {    EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16);  } +void Mips64Assembler::Dahi(GpuRegister rs, uint16_t imm16) { +  EmitI(1, rs, static_cast<GpuRegister>(6), imm16); +} + +void Mips64Assembler::Dati(GpuRegister rs, uint16_t imm16) { +  EmitI(1, rs, static_cast<GpuRegister>(0x1e), imm16); +} + +void Mips64Assembler::Sync(uint32_t stype) { +  EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), +           static_cast<GpuRegister>(0), stype & 0x1f, 0xf); +} +  void Mips64Assembler::Mfhi(GpuRegister rd) {    EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rd, 0, 0x10);  } @@ -321,34 +394,121 @@ void Mips64Assembler::Sltiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {    EmitI(0xb, rs, rt, imm16);  } -void Mips64Assembler::Beq(GpuRegister rt, GpuRegister rs, uint16_t imm16) { +void Mips64Assembler::Beq(GpuRegister rs, GpuRegister rt, uint16_t imm16) {    EmitI(0x4, rs, rt, imm16);    Nop();  } -void Mips64Assembler::Bne(GpuRegister rt, GpuRegister rs, uint16_t imm16) { +void Mips64Assembler::Bne(GpuRegister rs, GpuRegister rt, uint16_t imm16) {    EmitI(0x5, rs, rt, imm16);    Nop();  } -void Mips64Assembler::J(uint32_t address) { -  EmitJ(0x2, address); +void Mips64Assembler::J(uint32_t addr26) { +  EmitJ(0x2, addr26);    Nop();  } -void Mips64Assembler::Jal(uint32_t address) { -  EmitJ(0x2, address); +void Mips64Assembler::Jal(uint32_t addr26) { +  EmitJ(0x3, addr26);    Nop();  } -void Mips64Assembler::Jr(GpuRegister rs) { -  EmitR(0, rs, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), 0, 0x09);  // Jalr zero, rs +void Mips64Assembler::Jalr(GpuRegister rd, GpuRegister rs) { +  EmitR(0, rs, static_cast<GpuRegister>(0), rd, 0, 0x09);    Nop();  }  void Mips64Assembler::Jalr(GpuRegister rs) { -  EmitR(0, rs, static_cast<GpuRegister>(0), RA, 0, 0x09); -  Nop(); +  Jalr(RA, rs); +} + +void Mips64Assembler::Jr(GpuRegister rs) { +  Jalr(ZERO, rs); +} + +void Mips64Assembler::Auipc(GpuRegister rs, uint16_t imm16) { +  EmitI(0x3B, rs, static_cast<GpuRegister>(0x1E), imm16); +} + +void Mips64Assembler::Jic(GpuRegister rt, uint16_t imm16) { +  EmitI(0x36, static_cast<GpuRegister>(0), rt, imm16); +} + +void Mips64Assembler::Jialc(GpuRegister rt, uint16_t imm16) { +  EmitI(0x3E, static_cast<GpuRegister>(0), rt, imm16); +} + +void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rs, ZERO); +  CHECK_NE(rt, ZERO); +  CHECK_NE(rs, rt); +  EmitI(0x17, rs, rt, imm16); +} + +void Mips64Assembler::Bltzc(GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rt, ZERO); +  EmitI(0x17, rt, rt, imm16); +} + +void Mips64Assembler::Bgtzc(GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rt, ZERO); +  EmitI(0x17, static_cast<GpuRegister>(0), rt, imm16); +} + +void Mips64Assembler::Bgec(GpuRegister rs, GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rs, ZERO); +  CHECK_NE(rt, ZERO); +  CHECK_NE(rs, rt); +  EmitI(0x16, rs, rt, imm16); +} + +void Mips64Assembler::Bgezc(GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rt, ZERO); +  EmitI(0x16, rt, rt, imm16); +} + +void Mips64Assembler::Blezc(GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rt, ZERO); +  EmitI(0x16, static_cast<GpuRegister>(0), rt, imm16); +} + +void Mips64Assembler::Bltuc(GpuRegister rs, GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rs, ZERO); +  CHECK_NE(rt, ZERO); +  CHECK_NE(rs, rt); +  EmitI(0x7, rs, rt, imm16); +} + +void Mips64Assembler::Bgeuc(GpuRegister rs, GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rs, ZERO); +  CHECK_NE(rt, ZERO); +  CHECK_NE(rs, rt); +  EmitI(0x6, rs, rt, imm16); +} + +void Mips64Assembler::Beqc(GpuRegister rs, GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rs, ZERO); +  CHECK_NE(rt, ZERO); +  CHECK_NE(rs, rt); +  EmitI(0x8, (rs < rt) ? rs : rt, (rs < rt) ? rt : rs, imm16); +} + +void Mips64Assembler::Bnec(GpuRegister rs, GpuRegister rt, uint16_t imm16) { +  CHECK_NE(rs, ZERO); +  CHECK_NE(rt, ZERO); +  CHECK_NE(rs, rt); +  EmitI(0x18, (rs < rt) ? rs : rt, (rs < rt) ? rt : rs, imm16); +} + +void Mips64Assembler::Beqzc(GpuRegister rs, uint32_t imm21) { +  CHECK_NE(rs, ZERO); +  EmitI21(0x36, rs, imm21); +} + +void Mips64Assembler::Bnezc(GpuRegister rs, uint32_t imm21) { +  CHECK_NE(rs, ZERO); +  EmitI21(0x3E, rs, imm21);  }  void Mips64Assembler::AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft) { @@ -368,23 +528,19 @@ void Mips64Assembler::DivS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {  }  void Mips64Assembler::AddD(FpuRegister fd, FpuRegister fs, FpuRegister ft) { -  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs), -         static_cast<FpuRegister>(fd), 0x0); +  EmitFR(0x11, 0x11, ft, fs, fd, 0x0);  }  void Mips64Assembler::SubD(FpuRegister fd, FpuRegister fs, FpuRegister ft) { -  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs), -         static_cast<FpuRegister>(fd), 0x1); +  EmitFR(0x11, 0x11, ft, fs, fd, 0x1);  }  void Mips64Assembler::MulD(FpuRegister fd, FpuRegister fs, FpuRegister ft) { -  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs), -         static_cast<FpuRegister>(fd), 0x2); +  EmitFR(0x11, 0x11, ft, fs, fd, 0x2);  }  void Mips64Assembler::DivD(FpuRegister fd, FpuRegister fs, FpuRegister ft) { -  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs), -         static_cast<FpuRegister>(fd), 0x3); +  EmitFR(0x11, 0x11, ft, fs, fd, 0x3);  }  void Mips64Assembler::MovS(FpuRegister fd, FpuRegister fs) { @@ -392,16 +548,47 @@ void Mips64Assembler::MovS(FpuRegister fd, FpuRegister fs) {  }  void Mips64Assembler::MovD(FpuRegister fd, FpuRegister fs) { -  EmitFR(0x11, 0x11, static_cast<FpuRegister>(0), static_cast<FpuRegister>(fs), -         static_cast<FpuRegister>(fd), 0x6); +  EmitFR(0x11, 0x11, static_cast<FpuRegister>(0), fs, fd, 0x6); +} + +void Mips64Assembler::NegS(FpuRegister fd, FpuRegister fs) { +  EmitFR(0x11, 0x10, static_cast<FpuRegister>(0), fs, fd, 0x7); +} + +void Mips64Assembler::NegD(FpuRegister fd, FpuRegister fs) { +  EmitFR(0x11, 0x11, static_cast<FpuRegister>(0), fs, fd, 0x7); +} + +void Mips64Assembler::Cvtsw(FpuRegister fd, FpuRegister fs) { +  EmitFR(0x11, 0x14, static_cast<FpuRegister>(0), fs, fd, 0x20); +} + +void Mips64Assembler::Cvtdw(FpuRegister fd, FpuRegister fs) { +  EmitFR(0x11, 0x14, static_cast<FpuRegister>(0), fs, fd, 0x21); +} + +void Mips64Assembler::Cvtsd(FpuRegister fd, FpuRegister fs) { +  EmitFR(0x11, 0x11, static_cast<FpuRegister>(0), fs, fd, 0x20); +} + +void Mips64Assembler::Cvtds(FpuRegister fd, FpuRegister fs) { +  EmitFR(0x11, 0x10, static_cast<FpuRegister>(0), fs, fd, 0x21);  }  void Mips64Assembler::Mfc1(GpuRegister rt, FpuRegister fs) {    EmitFR(0x11, 0x00, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);  } -void Mips64Assembler::Mtc1(FpuRegister ft, GpuRegister rs) { -  EmitFR(0x11, 0x04, ft, static_cast<FpuRegister>(rs), static_cast<FpuRegister>(0), 0x0); +void Mips64Assembler::Mtc1(GpuRegister rt, FpuRegister fs) { +  EmitFR(0x11, 0x04, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0); +} + +void Mips64Assembler::Dmfc1(GpuRegister rt, FpuRegister fs) { +  EmitFR(0x11, 0x01, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0); +} + +void Mips64Assembler::Dmtc1(GpuRegister rt, FpuRegister fs) { +  EmitFR(0x11, 0x05, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);  }  void Mips64Assembler::Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) { @@ -430,45 +617,330 @@ void Mips64Assembler::Nop() {          static_cast<GpuRegister>(0), 0, 0x0);  } -void Mips64Assembler::Move(GpuRegister rt, GpuRegister rs) { -  EmitI(0x19, rs, rt, 0);   // Daddiu +void Mips64Assembler::Move(GpuRegister rd, GpuRegister rs) { +  Or(rd, rs, ZERO);  } -void Mips64Assembler::Clear(GpuRegister rt) { -  EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rt, 0, 0x20); +void Mips64Assembler::Clear(GpuRegister rd) { +  Move(rd, ZERO);  } -void Mips64Assembler::Not(GpuRegister rt, GpuRegister rs) { -  EmitR(0, static_cast<GpuRegister>(0), rs, rt, 0, 0x27); +void Mips64Assembler::Not(GpuRegister rd, GpuRegister rs) { +  Nor(rd, rs, ZERO);  } -void Mips64Assembler::Mul(GpuRegister rd, GpuRegister rs, GpuRegister rt) { -  Mult(rs, rt); -  Mflo(rd); +void Mips64Assembler::LoadConst32(GpuRegister rd, int32_t value) { +  if (IsUint<16>(value)) { +    // Use OR with (unsigned) immediate to encode 16b unsigned int. +    Ori(rd, ZERO, value); +  } else if (IsInt<16>(value)) { +    // Use ADD with (signed) immediate to encode 16b signed int. +    Addiu(rd, ZERO, value); +  } else { +    Lui(rd, value >> 16); +    if (value & 0xFFFF) +      Ori(rd, rd, value); +  }  } -void Mips64Assembler::Div(GpuRegister rd, GpuRegister rs, GpuRegister rt) { -  Div(rs, rt); -  Mflo(rd); +void Mips64Assembler::LoadConst64(GpuRegister rd, int64_t value) { +  int bit31 = (value & UINT64_C(0x80000000)) != 0; + +  // Loads with 1 instruction. +  if (IsUint<16>(value)) { +    Ori(rd, ZERO, value); +  } else if (IsInt<16>(value)) { +    Daddiu(rd, ZERO, value); +  } else if ((value & 0xFFFF) == 0 && IsInt<16>(value >> 16)) { +    Lui(rd, value >> 16); +  } else if (IsInt<32>(value)) { +    // Loads with 2 instructions. +    Lui(rd, value >> 16); +    Ori(rd, rd, value); +  } else if ((value & 0xFFFF0000) == 0 && IsInt<16>(value >> 32)) { +    Ori(rd, ZERO, value); +    Dahi(rd, value >> 32); +  } else if ((value & UINT64_C(0xFFFFFFFF0000)) == 0) { +    Ori(rd, ZERO, value); +    Dati(rd, value >> 48); +  } else if ((value & 0xFFFF) == 0 && +             (-32768 - bit31) <= (value >> 32) && (value >> 32) <= (32767 - bit31)) { +    Lui(rd, value >> 16); +    Dahi(rd, (value >> 32) + bit31); +  } else if ((value & 0xFFFF) == 0 && ((value >> 31) & 0x1FFFF) == ((0x20000 - bit31) & 0x1FFFF)) { +    Lui(rd, value >> 16); +    Dati(rd, (value >> 48) + bit31); +  } else { +    int shift_cnt = CTZ(value); +    int64_t tmp = value >> shift_cnt; +    if (IsUint<16>(tmp)) { +      Ori(rd, ZERO, tmp); +      if (shift_cnt < 32) +        Dsll(rd, rd, shift_cnt); +      else +        Dsll32(rd, rd, shift_cnt & 31); +    } else if (IsInt<16>(tmp)) { +      Daddiu(rd, ZERO, tmp); +      if (shift_cnt < 32) +        Dsll(rd, rd, shift_cnt); +      else +        Dsll32(rd, rd, shift_cnt & 31); +    } else if (IsInt<32>(tmp)) { +      // Loads with 3 instructions. +      Lui(rd, tmp >> 16); +      Ori(rd, rd, tmp); +      if (shift_cnt < 32) +        Dsll(rd, rd, shift_cnt); +      else +        Dsll32(rd, rd, shift_cnt & 31); +    } else { +      shift_cnt = 16 + CTZ(value >> 16); +      tmp = value >> shift_cnt; +      if (IsUint<16>(tmp)) { +        Ori(rd, ZERO, tmp); +        if (shift_cnt < 32) +          Dsll(rd, rd, shift_cnt); +        else +          Dsll32(rd, rd, shift_cnt & 31); +        Ori(rd, rd, value); +      } else if (IsInt<16>(tmp)) { +        Daddiu(rd, ZERO, tmp); +        if (shift_cnt < 32) +          Dsll(rd, rd, shift_cnt); +        else +          Dsll32(rd, rd, shift_cnt & 31); +        Ori(rd, rd, value); +      } else { +        // Loads with 3-4 instructions. +        uint64_t tmp2 = value; +        bool used_lui = false; +        if (((tmp2 >> 16) & 0xFFFF) != 0 || (tmp2 & 0xFFFFFFFF) == 0) { +          Lui(rd, tmp2 >> 16); +          used_lui = true; +        } +        if ((tmp2 & 0xFFFF) != 0) { +          if (used_lui) +            Ori(rd, rd, tmp2); +          else +            Ori(rd, ZERO, tmp2); +        } +        if (bit31) { +          tmp2 += UINT64_C(0x100000000); +        } +        if (((tmp2 >> 32) & 0xFFFF) != 0) { +          Dahi(rd, tmp2 >> 32); +        } +        if (tmp2 & UINT64_C(0x800000000000)) { +          tmp2 += UINT64_C(0x1000000000000); +        } +        if ((tmp2 >> 48) != 0) { +          Dati(rd, tmp2 >> 48); +        } +      } +    } +  }  } -void Mips64Assembler::Rem(GpuRegister rd, GpuRegister rs, GpuRegister rt) { -  Div(rs, rt); -  Mfhi(rd); +void Mips64Assembler::Addiu32(GpuRegister rt, GpuRegister rs, int32_t value, GpuRegister rtmp) { +  if (IsInt<16>(value)) { +    Addiu(rt, rs, value); +  } else { +    LoadConst32(rtmp, value); +    Addu(rt, rs, rtmp); +  }  } -void Mips64Assembler::AddConstant64(GpuRegister rt, GpuRegister rs, int32_t value) { -  CHECK((value >= -32768) && (value <= 32766)); -  Daddiu(rt, rs, value); +void Mips64Assembler::Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp) { +  if (IsInt<16>(value)) { +    Daddiu(rt, rs, value); +  } else { +    LoadConst64(rtmp, value); +    Daddu(rt, rs, rtmp); +  }  } -void Mips64Assembler::LoadImmediate64(GpuRegister rt, int32_t value) { -  CHECK((value >= -32768) && (value <= 32766)); -  Daddiu(rt, ZERO, value); +// +// MIPS64R6 branches +// +// +// Unconditional (pc + 32-bit signed offset): +// +//   auipc    at, ofs_high +//   jic      at, ofs_low +//   // no delay/forbidden slot +// +// +// Conditional (pc + 32-bit signed offset): +// +//   b<cond>c   reg, +2      // skip next 2 instructions +//   auipc      at, ofs_high +//   jic        at, ofs_low +//   // no delay/forbidden slot +// +// +// Unconditional (pc + 32-bit signed offset) and link: +// +//   auipc    reg, ofs_high +//   daddiu   reg, ofs_low +//   jialc    reg, 0 +//   // no delay/forbidden slot +// +// +// TODO: use shorter instruction sequences whenever possible. +// + +void Mips64Assembler::Bind(Label* label) { +  CHECK(!label->IsBound()); +  int32_t bound_pc = buffer_.Size(); + +  // Walk the list of the branches (auipc + jic pairs) referring to and preceding this label. +  // Embed the previously unknown pc-relative addresses in them. +  while (label->IsLinked()) { +    int32_t position = label->Position(); +    // Extract the branch (instruction pair) +    uint32_t auipc = buffer_.Load<uint32_t>(position); +    uint32_t jic = buffer_.Load<uint32_t>(position + 4);  // actually, jic or daddiu + +    // Extract the location of the previous pair in the list (walking the list backwards; +    // the previous pair location was stored in the immediate operands of the instructions) +    int32_t prev = (auipc << 16) | (jic & 0xFFFF); + +    // Get the pc-relative address +    uint32_t offset = bound_pc - position; +    offset += (offset & 0x8000) << 1;  // account for sign extension in jic/daddiu + +    // Embed it in the two instructions +    auipc = (auipc & 0xFFFF0000) | (offset >> 16); +    jic = (jic & 0xFFFF0000) | (offset & 0xFFFF); + +    // Save the adjusted instructions +    buffer_.Store<uint32_t>(position, auipc); +    buffer_.Store<uint32_t>(position + 4, jic); + +    // On to the previous branch in the list... +    label->position_ = prev; +  } + +  // Now make the label object contain its own location +  // (it will be used by the branches referring to and following this label) +  label->BindTo(bound_pc); +} + +void Mips64Assembler::B(Label* label) { +  if (label->IsBound()) { +    // Branch backwards (to a preceding label), distance is known +    uint32_t offset = label->Position() - buffer_.Size(); +    CHECK_LE(static_cast<int32_t>(offset), 0); +    offset += (offset & 0x8000) << 1;  // account for sign extension in jic +    Auipc(AT, offset >> 16); +    Jic(AT, offset); +  } else { +    // Branch forward (to a following label), distance is unknown +    int32_t position = buffer_.Size(); +    // The first branch forward will have 0 in its pc-relative address (copied from label's +    // position). It will be the terminator of the list of forward-reaching branches. +    uint32_t prev = label->position_; +    Auipc(AT, prev >> 16); +    Jic(AT, prev); +    // Now make the link object point to the location of this branch +    // (this forms a linked list of branches preceding this label) +    label->LinkTo(position); +  } +} + +void Mips64Assembler::Jalr(Label* label, GpuRegister indirect_reg) { +  if (label->IsBound()) { +    // Branch backwards (to a preceding label), distance is known +    uint32_t offset = label->Position() - buffer_.Size(); +    CHECK_LE(static_cast<int32_t>(offset), 0); +    offset += (offset & 0x8000) << 1;  // account for sign extension in daddiu +    Auipc(indirect_reg, offset >> 16); +    Daddiu(indirect_reg, indirect_reg, offset); +    Jialc(indirect_reg, 0); +  } else { +    // Branch forward (to a following label), distance is unknown +    int32_t position = buffer_.Size(); +    // The first branch forward will have 0 in its pc-relative address (copied from label's +    // position). It will be the terminator of the list of forward-reaching branches. +    uint32_t prev = label->position_; +    Auipc(indirect_reg, prev >> 16); +    Daddiu(indirect_reg, indirect_reg, prev); +    Jialc(indirect_reg, 0); +    // Now make the link object point to the location of this branch +    // (this forms a linked list of branches preceding this label) +    label->LinkTo(position); +  } +} + +void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Label* label) { +  Bgec(rs, rt, 2); +  B(label); +} + +void Mips64Assembler::Bltzc(GpuRegister rt, Label* label) { +  Bgezc(rt, 2); +  B(label); +} + +void Mips64Assembler::Bgtzc(GpuRegister rt, Label* label) { +  Blezc(rt, 2); +  B(label); +} + +void Mips64Assembler::Bgec(GpuRegister rs, GpuRegister rt, Label* label) { +  Bltc(rs, rt, 2); +  B(label); +} + +void Mips64Assembler::Bgezc(GpuRegister rt, Label* label) { +  Bltzc(rt, 2); +  B(label); +} + +void Mips64Assembler::Blezc(GpuRegister rt, Label* label) { +  Bgtzc(rt, 2); +  B(label); +} + +void Mips64Assembler::Bltuc(GpuRegister rs, GpuRegister rt, Label* label) { +  Bgeuc(rs, rt, 2); +  B(label); +} + +void Mips64Assembler::Bgeuc(GpuRegister rs, GpuRegister rt, Label* label) { +  Bltuc(rs, rt, 2); +  B(label); +} + +void Mips64Assembler::Beqc(GpuRegister rs, GpuRegister rt, Label* label) { +  Bnec(rs, rt, 2); +  B(label); +} + +void Mips64Assembler::Bnec(GpuRegister rs, GpuRegister rt, Label* label) { +  Beqc(rs, rt, 2); +  B(label); +} + +void Mips64Assembler::Beqzc(GpuRegister rs, Label* label) { +  Bnezc(rs, 2); +  B(label); +} + +void Mips64Assembler::Bnezc(GpuRegister rs, Label* label) { +  Beqzc(rs, 2); +  B(label);  }  void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base,                                       int32_t offset) { +  if (!IsInt<16>(offset)) { +    LoadConst32(AT, offset); +    Daddu(AT, AT, base); +    base = AT; +    offset = 0; +  } +    switch (type) {      case kLoadSignedByte:        Lb(reg, base, offset); @@ -489,23 +961,25 @@ void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuR        Lwu(reg, base, offset);        break;      case kLoadDoubleword: -      // TODO: alignment issues ???        Ld(reg, base, offset);        break; -    default: -      LOG(FATAL) << "UNREACHABLE";    }  }  void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base,                                          int32_t offset) { -  CHECK((offset >= -32768) && (offset <= 32766)); +  if (!IsInt<16>(offset)) { +    LoadConst32(AT, offset); +    Daddu(AT, AT, base); +    base = AT; +    offset = 0; +  } +    switch (type) {      case kLoadWord:        Lwc1(reg, base, offset);        break;      case kLoadDoubleword: -      // TODO: alignment issues ???        Ldc1(reg, base, offset);        break;      default: @@ -542,6 +1016,13 @@ void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register,  void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base,                                      int32_t offset) { +  if (!IsInt<16>(offset)) { +    LoadConst32(AT, offset); +    Daddu(AT, AT, base); +    base = AT; +    offset = 0; +  } +    switch (type) {      case kStoreByte:        Sb(reg, base, offset); @@ -553,7 +1034,6 @@ void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuR        Sw(reg, base, offset);        break;      case kStoreDoubleword: -      // TODO: alignment issues ???        Sd(reg, base, offset);        break;      default: @@ -563,6 +1043,13 @@ void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuR  void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base,                                         int32_t offset) { +  if (!IsInt<16>(offset)) { +    LoadConst32(AT, offset); +    Daddu(AT, AT, base); +    base = AT; +    offset = 0; +  } +    switch (type) {      case kStoreWord:        Swc1(reg, base, offset); @@ -613,10 +1100,12 @@ void Mips64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,        // only increment stack offset.        offset += size;      } else if (reg.IsFpuRegister()) { -      StoreFpuToOffset((size == 4) ? kStoreWord : kStoreDoubleword, reg.AsFpuRegister(), SP, offset); +      StoreFpuToOffset((size == 4) ? kStoreWord : kStoreDoubleword, +          reg.AsFpuRegister(), SP, offset);        offset += size;      } else if (reg.IsGpuRegister()) { -      StoreToOffset((size == 4) ? kStoreWord : kStoreDoubleword, reg.AsGpuRegister(), SP, offset); +      StoreToOffset((size == 4) ? kStoreWord : kStoreDoubleword, +          reg.AsGpuRegister(), SP, offset);        offset += size;      }    } @@ -650,14 +1139,14 @@ void Mips64Assembler::RemoveFrame(size_t frame_size,  }  void Mips64Assembler::IncreaseFrameSize(size_t adjust) { -  CHECK_ALIGNED(adjust, kStackAlignment); -  AddConstant64(SP, SP, -adjust); +  CHECK_ALIGNED(adjust, kFramePointerSize); +  Daddiu64(SP, SP, static_cast<int32_t>(-adjust));    cfi_.AdjustCFAOffset(adjust);  }  void Mips64Assembler::DecreaseFrameSize(size_t adjust) { -  CHECK_ALIGNED(adjust, kStackAlignment); -  AddConstant64(SP, SP, adjust); +  CHECK_ALIGNED(adjust, kFramePointerSize); +  Daddiu64(SP, SP, static_cast<int32_t>(adjust));    cfi_.AdjustCFAOffset(-adjust);  } @@ -702,7 +1191,7 @@ void Mips64Assembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,                                              ManagedRegister mscratch) {    Mips64ManagedRegister scratch = mscratch.AsMips64();    CHECK(scratch.IsGpuRegister()) << scratch; -  LoadImmediate64(scratch.AsGpuRegister(), imm); +  LoadConst32(scratch.AsGpuRegister(), imm);    StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());  } @@ -710,7 +1199,9 @@ void Mips64Assembler::StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t im                                                 ManagedRegister mscratch) {    Mips64ManagedRegister scratch = mscratch.AsMips64();    CHECK(scratch.IsGpuRegister()) << scratch; -  LoadImmediate64(scratch.AsGpuRegister(), imm); +  // TODO: it's unclear wether 32 or 64 bits need to be stored (Arm64 and x86/x64 disagree?). +  // Is this function even referenced anywhere else in the code? +  LoadConst32(scratch.AsGpuRegister(), imm);    StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, dest.Int32Value());  } @@ -719,7 +1210,7 @@ void Mips64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> thr_offs,                                                   ManagedRegister mscratch) {    Mips64ManagedRegister scratch = mscratch.AsMips64();    CHECK(scratch.IsGpuRegister()) << scratch; -  AddConstant64(scratch.AsGpuRegister(), SP, fr_offs.Int32Value()); +  Daddiu64(scratch.AsGpuRegister(), SP, fr_offs.Int32Value());    StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, thr_offs.Int32Value());  } @@ -757,20 +1248,24 @@ void Mips64Assembler::LoadRef(ManagedRegister mdest, ManagedRegister base, Membe    LoadFromOffset(kLoadUnsignedWord, dest.AsGpuRegister(),                   base.AsMips64().AsGpuRegister(), offs.Int32Value());    if (kPoisonHeapReferences && poison_reference) { -    Subu(dest.AsGpuRegister(), ZERO, dest.AsGpuRegister()); +    // TODO: review +    // Negate the 32-bit ref +    Dsubu(dest.AsGpuRegister(), ZERO, dest.AsGpuRegister()); +    // And constrain it to 32 bits (zero-extend into bits 32 through 63) as on Arm64 and x86/64 +    Dext(dest.AsGpuRegister(), dest.AsGpuRegister(), 0, 31);    }  }  void Mips64Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, -                               Offset offs) { +                                 Offset offs) {    Mips64ManagedRegister dest = mdest.AsMips64(); -  CHECK(dest.IsGpuRegister() && dest.IsGpuRegister()) << dest; +  CHECK(dest.IsGpuRegister() && base.AsMips64().IsGpuRegister());    LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(),                   base.AsMips64().AsGpuRegister(), offs.Int32Value());  }  void Mips64Assembler::LoadRawPtrFromThread64(ManagedRegister mdest, -                                         ThreadOffset<8> offs) { +                                             ThreadOffset<8> offs) {    Mips64ManagedRegister dest = mdest.AsMips64();    CHECK(dest.IsGpuRegister());    LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(), S1, offs.Int32Value()); @@ -849,7 +1344,7 @@ void Mips64Assembler::Copy(FrameOffset dest, FrameOffset src,  }  void Mips64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, -                         ManagedRegister mscratch, size_t size) { +                           ManagedRegister mscratch, size_t size) {    GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();    CHECK(size == 4 || size == 8) << size;    if (size == 4) { @@ -866,7 +1361,7 @@ void Mips64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset sr  }  void Mips64Assembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, -                         ManagedRegister mscratch, size_t size) { +                           ManagedRegister mscratch, size_t size) {    GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();    CHECK(size == 4 || size == 8) << size;    if (size == 4) { @@ -888,8 +1383,8 @@ void Mips64Assembler::Copy(FrameOffset /*dest*/, FrameOffset /*src_base*/, Offse  }  void Mips64Assembler::Copy(ManagedRegister dest, Offset dest_offset, -                         ManagedRegister src, Offset src_offset, -                         ManagedRegister mscratch, size_t size) { +                           ManagedRegister src, Offset src_offset, +                           ManagedRegister mscratch, size_t size) {    GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();    CHECK(size == 4 || size == 8) << size;    if (size == 4) { @@ -912,12 +1407,14 @@ void Mips64Assembler::Copy(FrameOffset /*dest*/, Offset /*dest_offset*/, FrameOf  }  void Mips64Assembler::MemoryBarrier(ManagedRegister) { +  // TODO: sync?    UNIMPLEMENTED(FATAL) << "no mips64 implementation";  }  void Mips64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg, -                                    FrameOffset handle_scope_offset, -                                    ManagedRegister min_reg, bool null_allowed) { +                                             FrameOffset handle_scope_offset, +                                             ManagedRegister min_reg, +                                             bool null_allowed) {    Mips64ManagedRegister out_reg = mout_reg.AsMips64();    Mips64ManagedRegister in_reg = min_reg.AsMips64();    CHECK(in_reg.IsNoRegister() || in_reg.IsGpuRegister()) << in_reg; @@ -933,20 +1430,20 @@ void Mips64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg,        in_reg = out_reg;      }      if (!out_reg.Equals(in_reg)) { -      LoadImmediate64(out_reg.AsGpuRegister(), 0); +      LoadConst32(out_reg.AsGpuRegister(), 0);      } -    EmitBranch(in_reg.AsGpuRegister(), ZERO, &null_arg, true); -    AddConstant64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value()); -    Bind(&null_arg, false); +    Beqzc(in_reg.AsGpuRegister(), &null_arg); +    Daddiu64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value()); +    Bind(&null_arg);    } else { -    AddConstant64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value()); +    Daddiu64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value());    }  }  void Mips64Assembler::CreateHandleScopeEntry(FrameOffset out_off, -                                    FrameOffset handle_scope_offset, -                                    ManagedRegister mscratch, -                                    bool null_allowed) { +                                             FrameOffset handle_scope_offset, +                                             ManagedRegister mscratch, +                                             bool null_allowed) {    Mips64ManagedRegister scratch = mscratch.AsMips64();    CHECK(scratch.IsGpuRegister()) << scratch;    if (null_allowed) { @@ -956,30 +1453,30 @@ void Mips64Assembler::CreateHandleScopeEntry(FrameOffset out_off,      // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is      // the address in the handle scope holding the reference.      // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) -    EmitBranch(scratch.AsGpuRegister(), ZERO, &null_arg, true); -    AddConstant64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value()); -    Bind(&null_arg, false); +    Beqzc(scratch.AsGpuRegister(), &null_arg); +    Daddiu64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value()); +    Bind(&null_arg);    } else { -    AddConstant64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value()); +    Daddiu64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value());    }    StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, out_off.Int32Value());  }  // Given a handle scope entry, load the associated reference.  void Mips64Assembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg, -                                          ManagedRegister min_reg) { +                                                   ManagedRegister min_reg) {    Mips64ManagedRegister out_reg = mout_reg.AsMips64();    Mips64ManagedRegister in_reg = min_reg.AsMips64();    CHECK(out_reg.IsGpuRegister()) << out_reg;    CHECK(in_reg.IsGpuRegister()) << in_reg;    Label null_arg;    if (!out_reg.Equals(in_reg)) { -    LoadImmediate64(out_reg.AsGpuRegister(), 0); +    LoadConst32(out_reg.AsGpuRegister(), 0);    } -  EmitBranch(in_reg.AsGpuRegister(), ZERO, &null_arg, true); +  Beqzc(in_reg.AsGpuRegister(), &null_arg);    LoadFromOffset(kLoadDoubleword, out_reg.AsGpuRegister(),                   in_reg.AsGpuRegister(), 0); -  Bind(&null_arg, false); +  Bind(&null_arg);  }  void Mips64Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { @@ -1022,7 +1519,7 @@ void Mips64Assembler::GetCurrentThread(ManagedRegister tr) {  }  void Mips64Assembler::GetCurrentThread(FrameOffset offset, -                                     ManagedRegister /*mscratch*/) { +                                       ManagedRegister /*mscratch*/) {    StoreToOffset(kStoreDoubleword, S1, SP, offset.Int32Value());  } @@ -1032,13 +1529,13 @@ void Mips64Assembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjus    buffer_.EnqueueSlowPath(slow);    LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),                   S1, Thread::ExceptionOffset<8>().Int32Value()); -  EmitBranch(scratch.AsGpuRegister(), ZERO, slow->Entry(), false); +  Bnezc(scratch.AsGpuRegister(), slow->Entry());  }  void Mips64ExceptionSlowPath::Emit(Assembler* sasm) {    Mips64Assembler* sp_asm = down_cast<Mips64Assembler*>(sasm);  #define __ sp_asm-> -  __ Bind(&entry_, false); +  __ Bind(&entry_);    if (stack_adjust_ != 0) {  // Fix up the frame.      __ DecreaseFrameSize(stack_adjust_);    } @@ -1048,6 +1545,7 @@ void Mips64ExceptionSlowPath::Emit(Assembler* sasm) {    // Set up call to Thread::Current()->pDeliverException    __ LoadFromOffset(kLoadDoubleword, T9, S1,                      QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()); +  // TODO: check T9 usage    __ Jr(T9);    // Call never returns    __ Break(); diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 38419abbac..88cc4bcd00 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -56,13 +56,31 @@ class Mips64Assembler FINAL : public Assembler {    void Addi(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Addu(GpuRegister rd, GpuRegister rs, GpuRegister rt);    void Addiu(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16); +  void Daddu(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 +  void Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);  // MIPS64    void Sub(GpuRegister rd, GpuRegister rs, GpuRegister rt);    void Subu(GpuRegister rd, GpuRegister rs, GpuRegister rt); -  void Mult(GpuRegister rs, GpuRegister rt); -  void Multu(GpuRegister rs, GpuRegister rt); -  void Div(GpuRegister rs, GpuRegister rt); -  void Divu(GpuRegister rs, GpuRegister rt); +  void Dsubu(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 + +  void MultR2(GpuRegister rs, GpuRegister rt);  // R2 +  void MultuR2(GpuRegister rs, GpuRegister rt);  // R2 +  void DivR2(GpuRegister rs, GpuRegister rt);  // R2 +  void DivuR2(GpuRegister rs, GpuRegister rt);  // R2 +  void MulR2(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R2 +  void DivR2(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R2 +  void ModR2(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R2 +  void DivuR2(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R2 +  void ModuR2(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R2 +  void MulR6(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R6 +  void DivR6(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R6 +  void ModR6(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R6 +  void DivuR6(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R6 +  void ModuR6(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // R6 +  void Dmul(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 R6 +  void Ddiv(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 R6 +  void Dmod(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 R6 +  void Ddivu(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 R6 +  void Dmodu(GpuRegister rd, GpuRegister rs, GpuRegister rt);  // MIPS64 R6    void And(GpuRegister rd, GpuRegister rs, GpuRegister rt);    void Andi(GpuRegister rt, GpuRegister rs, uint16_t imm16); @@ -72,40 +90,72 @@ class Mips64Assembler FINAL : public Assembler {    void Xori(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt); -  void Sll(GpuRegister rd, GpuRegister rs, int shamt); -  void Srl(GpuRegister rd, GpuRegister rs, int shamt); -  void Sra(GpuRegister rd, GpuRegister rs, int shamt); -  void Sllv(GpuRegister rd, GpuRegister rs, GpuRegister rt); -  void Srlv(GpuRegister rd, GpuRegister rs, GpuRegister rt); -  void Srav(GpuRegister rd, GpuRegister rs, GpuRegister rt); +  void Seb(GpuRegister rd, GpuRegister rt);  // R2+ +  void Seh(GpuRegister rd, GpuRegister rt);  // R2+ +  void Dext(GpuRegister rs, GpuRegister rt, int pos, int size_less_one);  // MIPS64 + +  void Sll(GpuRegister rd, GpuRegister rt, int shamt); +  void Srl(GpuRegister rd, GpuRegister rt, int shamt); +  void Sra(GpuRegister rd, GpuRegister rt, int shamt); +  void Sllv(GpuRegister rd, GpuRegister rt, GpuRegister rs); +  void Srlv(GpuRegister rd, GpuRegister rt, GpuRegister rs); +  void Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs); +  void Dsll(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64 +  void Dsrl(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64 +  void Dsra(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64 +  void Dsll32(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64 +  void Dsrl32(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64 +  void Dsra32(GpuRegister rd, GpuRegister rt, int shamt);  // MIPS64 +  void Dsllv(GpuRegister rd, GpuRegister rt, GpuRegister rs);  // MIPS64 +  void Dsrlv(GpuRegister rd, GpuRegister rt, GpuRegister rs);  // MIPS64 +  void Dsrav(GpuRegister rd, GpuRegister rt, GpuRegister rs);  // MIPS64    void Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Lh(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Lw(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16); +  void Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16);  // MIPS64    void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16); +  void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16);  // MIPS64    void Lui(GpuRegister rt, uint16_t imm16); -  void Mfhi(GpuRegister rd); -  void Mflo(GpuRegister rd); +  void Dahi(GpuRegister rs, uint16_t imm16);  // MIPS64 R6 +  void Dati(GpuRegister rs, uint16_t imm16);  // MIPS64 R6 +  void Sync(uint32_t stype); +  void Mfhi(GpuRegister rd);  // R2 +  void Mflo(GpuRegister rd);  // R2    void Sb(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Sh(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Sw(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void Sd(GpuRegister rt, GpuRegister rs, uint16_t imm16); +  void Sd(GpuRegister rt, GpuRegister rs, uint16_t imm16);  // MIPS64    void Slt(GpuRegister rd, GpuRegister rs, GpuRegister rt);    void Sltu(GpuRegister rd, GpuRegister rs, GpuRegister rt);    void Slti(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Sltiu(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void Beq(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void Bne(GpuRegister rt, GpuRegister rs, uint16_t imm16); -  void J(uint32_t address); -  void Jal(uint32_t address); -  void Jr(GpuRegister rs); +  void Beq(GpuRegister rs, GpuRegister rt, uint16_t imm16); +  void Bne(GpuRegister rs, GpuRegister rt, uint16_t imm16); +  void J(uint32_t addr26); +  void Jal(uint32_t addr26); +  void Jalr(GpuRegister rd, GpuRegister rs);    void Jalr(GpuRegister rs); +  void Jr(GpuRegister rs); +  void Auipc(GpuRegister rs, uint16_t imm16);  // R6 +  void Jic(GpuRegister rt, uint16_t imm16);  // R6 +  void Jialc(GpuRegister rt, uint16_t imm16);  // R6 +  void Bltc(GpuRegister rs, GpuRegister rt, uint16_t imm16);  // R6 +  void Bltzc(GpuRegister rt, uint16_t imm16);  // R6 +  void Bgtzc(GpuRegister rt, uint16_t imm16);  // R6 +  void Bgec(GpuRegister rs, GpuRegister rt, uint16_t imm16);  // R6 +  void Bgezc(GpuRegister rt, uint16_t imm16);  // R6 +  void Blezc(GpuRegister rt, uint16_t imm16);  // R6 +  void Bltuc(GpuRegister rs, GpuRegister rt, uint16_t imm16);  // R6 +  void Bgeuc(GpuRegister rs, GpuRegister rt, uint16_t imm16);  // R6 +  void Beqc(GpuRegister rs, GpuRegister rt, uint16_t imm16);  // R6 +  void Bnec(GpuRegister rs, GpuRegister rt, uint16_t imm16);  // R6 +  void Beqzc(GpuRegister rs, uint32_t imm21);  // R6 +  void Bnezc(GpuRegister rs, uint32_t imm21);  // R6    void AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft);    void SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft); @@ -117,9 +167,18 @@ class Mips64Assembler FINAL : public Assembler {    void DivD(FpuRegister fd, FpuRegister fs, FpuRegister ft);    void MovS(FpuRegister fd, FpuRegister fs);    void MovD(FpuRegister fd, FpuRegister fs); +  void NegS(FpuRegister fd, FpuRegister fs); +  void NegD(FpuRegister fd, FpuRegister fs); + +  void Cvtsw(FpuRegister fd, FpuRegister fs); +  void Cvtdw(FpuRegister fd, FpuRegister fs); +  void Cvtsd(FpuRegister fd, FpuRegister fs); +  void Cvtds(FpuRegister fd, FpuRegister fs);    void Mfc1(GpuRegister rt, FpuRegister fs); -  void Mtc1(FpuRegister ft, GpuRegister rs); +  void Mtc1(GpuRegister rt, FpuRegister fs); +  void Dmfc1(GpuRegister rt, FpuRegister fs);  // MIPS64 +  void Dmtc1(GpuRegister rt, FpuRegister fs);  // MIPS64    void Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);    void Ldc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);    void Swc1(FpuRegister ft, GpuRegister rs, uint16_t imm16); @@ -127,15 +186,33 @@ class Mips64Assembler FINAL : public Assembler {    void Break();    void Nop(); -  void Move(GpuRegister rt, GpuRegister rs); -  void Clear(GpuRegister rt); -  void Not(GpuRegister rt, GpuRegister rs); -  void Mul(GpuRegister rd, GpuRegister rs, GpuRegister rt); -  void Div(GpuRegister rd, GpuRegister rs, GpuRegister rt); -  void Rem(GpuRegister rd, GpuRegister rs, GpuRegister rt); - -  void AddConstant64(GpuRegister rt, GpuRegister rs, int32_t value); -  void LoadImmediate64(GpuRegister rt, int32_t value); +  void Move(GpuRegister rd, GpuRegister rs); +  void Clear(GpuRegister rd); +  void Not(GpuRegister rd, GpuRegister rs); + +  // Higher level composite instructions +  void LoadConst32(GpuRegister rd, int32_t value); +  void LoadConst64(GpuRegister rd, int64_t value);  // MIPS64 + +  void Addiu32(GpuRegister rt, GpuRegister rs, int32_t value, GpuRegister rtmp = AT); +  void Daddiu64(GpuRegister rt, GpuRegister rs, int64_t value, GpuRegister rtmp = AT);  // MIPS64 + +  void Bind(Label* label);  // R6 +  void B(Label* label);  // R6 +  void Jalr(Label* label, GpuRegister indirect_reg = RA);  // R6 +  // TODO: implement common for R6 and non-R6 interface for conditional branches? +  void Bltc(GpuRegister rs, GpuRegister rt, Label* label);  // R6 +  void Bltzc(GpuRegister rt, Label* label);  // R6 +  void Bgtzc(GpuRegister rt, Label* label);  // R6 +  void Bgec(GpuRegister rs, GpuRegister rt, Label* label);  // R6 +  void Bgezc(GpuRegister rt, Label* label);  // R6 +  void Blezc(GpuRegister rt, Label* label);  // R6 +  void Bltuc(GpuRegister rs, GpuRegister rt, Label* label);  // R6 +  void Bgeuc(GpuRegister rs, GpuRegister rt, Label* label);  // R6 +  void Beqc(GpuRegister rs, GpuRegister rt, Label* label);  // R6 +  void Bnec(GpuRegister rs, GpuRegister rt, Label* label);  // R6 +  void Beqzc(GpuRegister rs, Label* label);  // R6 +  void Bnezc(GpuRegister rs, Label* label);  // R6    void EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, size_t size);    void LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base, int32_t offset); @@ -144,10 +221,7 @@ class Mips64Assembler FINAL : public Assembler {    void StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base, int32_t offset);    // Emit data (e.g. encoded instruction or immediate) to the instruction stream. -  void Emit(int32_t value); -  void EmitBranch(GpuRegister rt, GpuRegister rs, Label* label, bool equal); -  void EmitJump(Label* label, bool link); -  void Bind(Label* label, bool is_jump); +  void Emit(uint32_t value);    //    // Overridden common assembler high-level functionality @@ -269,13 +343,11 @@ class Mips64Assembler FINAL : public Assembler {   private:    void EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegister rd, int shamt, int funct);    void EmitI(int opcode, GpuRegister rs, GpuRegister rt, uint16_t imm); -  void EmitJ(int opcode, int address); +  void EmitI21(int opcode, GpuRegister rs, uint32_t imm21); +  void EmitJ(int opcode, uint32_t addr26);    void EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd, int funct);    void EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm); -  int32_t EncodeBranchOffset(int offset, int32_t inst, bool is_jump); -  int DecodeBranchOffset(int32_t inst, bool is_jump); -    DISALLOW_COPY_AND_ASSIGN(Mips64Assembler);  }; diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h index 8b7697cac3..f57498d34f 100644 --- a/compiler/utils/mips64/constants_mips64.h +++ b/compiler/utils/mips64/constants_mips64.h @@ -67,7 +67,7 @@ class Instr {    static const uint32_t kBreakPointInstruction = 0x0000000D;    bool IsBreakPoint() { -    return ((*reinterpret_cast<const uint32_t*>(this)) & 0xFC0000CF) == kBreakPointInstruction; +    return ((*reinterpret_cast<const uint32_t*>(this)) & 0xFC00003F) == kBreakPointInstruction;    }    // Instructions are read out of a code stream. The only way to get a diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index ac8173773d..70ca88dc55 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -106,6 +106,9 @@ static const MipsInstruction gMipsInstructions[] = {    { kRTypeMask | (0x1f << 21), 62 | (1 << 21), "drotr32", "DTA", },    { kRTypeMask, 62, "dsrl32", "DTA", },    { kRTypeMask, 63, "dsra32", "DTA", }, +  { kRTypeMask, (31u << kOpcodeShift) | 3, "dext", "TSAZ", }, +  { kRTypeMask | (0x1f << 21) | (0x1f << 6), (31u << 26) | (16 << 6) | 32, "seb", "DT", }, +  { kRTypeMask | (0x1f << 21) | (0x1f << 6), (31u << 26) | (24 << 6) | 32, "seh", "DT", },    // SPECIAL0    { kSpecial0Mask | 0x7ff, (2 << 6) | 24, "mul", "DST" }, @@ -150,13 +153,17 @@ static const MipsInstruction gMipsInstructions[] = {    { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (16 << 16), "bltzal", "SB" },    { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (18 << 16), "bltzall", "SB" },    { kITypeMask | (0x1f << 16), 6 << kOpcodeShift | (0 << 16), "blez", "SB" }, +  { kITypeMask, 6 << kOpcodeShift, "bgeuc", "STB" },    { kITypeMask | (0x1f << 16), 7 << kOpcodeShift | (0 << 16), "bgtz", "SB" }, +  { kITypeMask, 7 << kOpcodeShift, "bltuc", "STB" },    { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (6 << 16), "dahi", "Si", },    { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (30 << 16), "dati", "Si", },    { 0xffff0000, (4 << kOpcodeShift), "b", "B" },    { 0xffff0000, (1 << kOpcodeShift) | (17 << 16), "bal", "B" }, +  { kITypeMask, 8 << kOpcodeShift, "beqc", "STB" }, +    { kITypeMask, 8 << kOpcodeShift, "addi", "TSi", },    { kITypeMask, 9 << kOpcodeShift, "addiu", "TSi", },    { kITypeMask, 10 << kOpcodeShift, "slti", "TSi", }, @@ -166,6 +173,83 @@ static const MipsInstruction gMipsInstructions[] = {    { kITypeMask, 14 << kOpcodeShift, "xori", "TSi", },    { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "TI", },    { kITypeMask, 15 << kOpcodeShift, "aui", "TSI", }, + +  { kITypeMask | (0x1f << 21), 22 << kOpcodeShift, "blezc", "TB" }, + +  // TODO: de-dup +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (1  << 21) | (1  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (2  << 21) | (2  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (3  << 21) | (3  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (4  << 21) | (4  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (5  << 21) | (5  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (6  << 21) | (6  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (7  << 21) | (7  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (8  << 21) | (8  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (9  << 21) | (9  << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (10 << 21) | (10 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (11 << 21) | (11 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (12 << 21) | (12 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (13 << 21) | (13 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (14 << 21) | (14 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (15 << 21) | (15 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (16 << 21) | (16 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (17 << 21) | (17 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (18 << 21) | (18 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (19 << 21) | (19 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (20 << 21) | (20 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (21 << 21) | (21 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (22 << 21) | (22 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (23 << 21) | (23 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (24 << 21) | (24 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (25 << 21) | (25 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (26 << 21) | (26 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (27 << 21) | (27 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (28 << 21) | (28 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (29 << 21) | (29 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (30 << 21) | (30 << 16), "bgezc", "TB" }, +  { kITypeMask | (0x3ff << 16), (22 << kOpcodeShift) | (31 << 21) | (31 << 16), "bgezc", "TB" }, + +  { kITypeMask, 22 << kOpcodeShift, "bgec", "STB" }, + +  { kITypeMask | (0x1f << 21), 23 << kOpcodeShift, "bgtzc", "TB" }, + +  // TODO: de-dup +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (1  << 21) | (1  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (2  << 21) | (2  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (3  << 21) | (3  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (4  << 21) | (4  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (5  << 21) | (5  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (6  << 21) | (6  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (7  << 21) | (7  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (8  << 21) | (8  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (9  << 21) | (9  << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (10 << 21) | (10 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (11 << 21) | (11 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (12 << 21) | (12 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (13 << 21) | (13 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (14 << 21) | (14 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (15 << 21) | (15 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (16 << 21) | (16 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (17 << 21) | (17 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (18 << 21) | (18 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (19 << 21) | (19 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (20 << 21) | (20 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (21 << 21) | (21 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (22 << 21) | (22 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (23 << 21) | (23 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (24 << 21) | (24 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (25 << 21) | (25 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (26 << 21) | (26 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (27 << 21) | (27 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (28 << 21) | (28 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (29 << 21) | (29 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (30 << 21) | (30 << 16), "bltzc", "TB" }, +  { kITypeMask | (0x3ff << 16), (23 << kOpcodeShift) | (31 << 21) | (31 << 16), "bltzc", "TB" }, + +  { kITypeMask, 23 << kOpcodeShift, "bltc", "STB" }, + +  { kITypeMask, 24 << kOpcodeShift, "bnec", "STB" }, +    { kITypeMask, 25 << kOpcodeShift, "daddiu", "TSi", },    { kITypeMask, 29 << kOpcodeShift, "daui", "TSi", }, @@ -180,9 +264,22 @@ static const MipsInstruction gMipsInstructions[] = {    { kITypeMask, 43u << kOpcodeShift, "sw", "TO", },    { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },    { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", }, +  { kITypeMask | (0x1f << 21), 54u << kOpcodeShift, "jic", "Ti" }, +  { kITypeMask | (1 << 21), (54u << kOpcodeShift) | (1 << 21), "beqzc", "Sb" },  // TODO: de-dup? +  { kITypeMask | (1 << 22), (54u << kOpcodeShift) | (1 << 22), "beqzc", "Sb" }, +  { kITypeMask | (1 << 23), (54u << kOpcodeShift) | (1 << 23), "beqzc", "Sb" }, +  { kITypeMask | (1 << 24), (54u << kOpcodeShift) | (1 << 24), "beqzc", "Sb" }, +  { kITypeMask | (1 << 25), (54u << kOpcodeShift) | (1 << 25), "beqzc", "Sb" },    { kITypeMask, 55u << kOpcodeShift, "ld", "TO", },    { kITypeMask, 57u << kOpcodeShift, "swc1", "tO", }, +  { kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" },    { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", }, +  { kITypeMask | (0x1f << 21), 62u << kOpcodeShift, "jialc", "Ti" }, +  { kITypeMask | (1 << 21), (62u << kOpcodeShift) | (1 << 21), "bnezc", "Sb" },  // TODO: de-dup? +  { kITypeMask | (1 << 22), (62u << kOpcodeShift) | (1 << 22), "bnezc", "Sb" }, +  { kITypeMask | (1 << 23), (62u << kOpcodeShift) | (1 << 23), "bnezc", "Sb" }, +  { kITypeMask | (1 << 24), (62u << kOpcodeShift) | (1 << 24), "bnezc", "Sb" }, +  { kITypeMask | (1 << 25), (62u << kOpcodeShift) | (1 << 25), "bnezc", "Sb" },    { kITypeMask, 63u << kOpcodeShift, "sd", "TO", },    // Floating point. @@ -241,7 +338,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {        opcode = gMipsInstructions[i].name;        for (const char* args_fmt = gMipsInstructions[i].args_fmt; *args_fmt; ++args_fmt) {          switch (*args_fmt) { -          case 'A':  // sa (shift amount). +          case 'A':  // sa (shift amount or [d]ext position).              args << sa;              break;            case 'B':  // Branch offset. @@ -253,6 +350,15 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {                     << StringPrintf("  ; %+d", offset);              }              break; +          case 'b':  // 21-bit branch offset. +            { +              int32_t offset = (instruction & 0x1fffff) - ((instruction & 0x100000) << 1); +              offset <<= 2; +              offset += 4;  // Delay slot. +              args << FormatInstructionPointer(instr_ptr + offset) +                   << StringPrintf("  ; %+d", offset); +            } +            break;            case 'D': args << 'r' << rd; break;            case 'd': args << 'f' << rd; break;            case 'a': args << 'f' << sa; break; @@ -302,6 +408,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {            case 's': args << 'f' << rs; break;            case 'T': args << 'r' << rt; break;            case 't': args << 'f' << rt; break; +          case 'Z': args << rd; break;   // sz ([d]ext size).          }          if (*(args_fmt + 1)) {            args << ", "; @@ -311,9 +418,36 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {      }    } +  // Special cases for sequences of: +  //   pc-relative +/- 2GB branch: +  //     auipc  reg, imm +  //     jic    reg, imm +  //   pc-relative +/- 2GB branch and link: +  //     auipc  reg, imm +  //     daddiu reg, reg, imm +  //     jialc  reg, 0 +  if (((op == 0x36 && rs == 0 && rt != 0) ||  // jic +       (op == 0x19 && rs == rt && rt != 0)) &&  // daddiu +      last_ptr_ && (intptr_t)instr_ptr - (intptr_t)last_ptr_ == 4 && +      (last_instr_ & 0xFC1F0000) == 0xEC1E0000 &&  // auipc +      ((last_instr_ >> 21) & 0x1F) == rt) { +    uint32_t offset = (last_instr_ << 16) | (instruction & 0xFFFF); +    offset -= (offset & 0x8000) << 1; +    offset -= 4; +    if (op == 0x36) { +      args << "  ; b "; +    } else { +      args << "  ; move r" << rt << ", "; +    } +    args << FormatInstructionPointer(instr_ptr + (int32_t)offset); +    args << StringPrintf("  ; %+d", (int32_t)offset); +  } +    os << FormatInstructionPointer(instr_ptr)       << StringPrintf(": %08x\t%-7s ", instruction, opcode.c_str())       << args.str() << '\n'; +  last_ptr_ = instr_ptr; +  last_instr_ = instruction;    return 4;  } diff --git a/disassembler/disassembler_mips.h b/disassembler/disassembler_mips.h index 67c3fcb71f..4f70a9b523 100644 --- a/disassembler/disassembler_mips.h +++ b/disassembler/disassembler_mips.h @@ -27,7 +27,9 @@ namespace mips {  class DisassemblerMips FINAL : public Disassembler {   public:    explicit DisassemblerMips(DisassemblerOptions* options, bool is64bit) : Disassembler(options), -      is64bit_(is64bit) {} +      is64bit_(is64bit), +      last_ptr_(nullptr), +      last_instr_(0) {}    size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE;    void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE; @@ -35,6 +37,11 @@ class DisassemblerMips FINAL : public Disassembler {   private:    const bool is64bit_; +  // Address and encoding of the last disassembled instruction. +  // Needed to produce more readable disassembly of certain 2-instruction sequences. +  const uint8_t* last_ptr_; +  uint32_t last_instr_; +    DISALLOW_COPY_AND_ASSIGN(DisassemblerMips);  }; diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 5eda539e7d..c19133c713 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -423,6 +423,37 @@ endif  TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := +# Known broken tests for the MIPS64 optimizing compiler backend in 64-bit mode.  b/21555893 +TEST_ART_BROKEN_OPTIMIZING_MIPS64_64BIT_RUN_TESTS := \ +  004-SignalTest \ +  018-stack-overflow \ +  107-int-math2 \ +  449-checker-bce + +ifeq ($(TARGET_ARCH),mips64) +  ifneq (,$(filter optimizing,$(COMPILER_TYPES))) +    ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ +        optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ +        $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_MIPS64_64BIT_RUN_TESTS),64) +  endif +endif + +TEST_ART_BROKEN_OPTIMIZING_MIPS64_64BIT_RUN_TESTS := + +# Known broken tests for the MIPS64 optimizing compiler backend in 32-bit mode.  b/21555893 +TEST_ART_BROKEN_OPTIMIZING_MIPS64_32BIT_RUN_TESTS := \ +  496-checker-inlining-and-class-loader + +ifeq ($(TARGET_ARCH),mips64) +  ifneq (,$(filter optimizing,$(COMPILER_TYPES))) +    ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ +        optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ +        $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_MIPS64_32BIT_RUN_TESTS),32) +  endif +endif + +TEST_ART_BROKEN_OPTIMIZING_MIPS64_32BIT_RUN_TESTS := +  # Known broken tests for the optimizing compiler.  TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=  |