diff options
Diffstat (limited to 'compiler/optimizing/code_generator_mips64.cc')
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 385 | 
1 files changed, 352 insertions, 33 deletions
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 985ac2ca554..5b07b55cbbb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1509,6 +1509,14 @@ inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches(    }  } +linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset, +                                              const DexFile* target_dex_file, +                                              uint32_t pc_insn_offset, +                                              uint32_t boot_image_offset) { +  DCHECK(target_dex_file == nullptr);  // Unused for DataBimgRelRoPatch(), should be null. +  return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset); +} +  void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {    DCHECK(linker_patches->empty());    size_t size = @@ -1527,11 +1535,10 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* li      EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(          boot_image_string_patches_, linker_patches);    } else { -    DCHECK(boot_image_method_patches_.empty()); -    EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( -        boot_image_type_patches_, linker_patches); -    EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( -        boot_image_string_patches_, linker_patches); +    EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>( +        boot_image_method_patches_, linker_patches); +    DCHECK(boot_image_type_patches_.empty()); +    DCHECK(boot_image_string_patches_.empty());    }    EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(        method_bss_entry_patches_, linker_patches); @@ -1542,6 +1549,13 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* li    DCHECK_EQ(size, linker_patches->size());  } +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageRelRoPatch( +    uint32_t boot_image_offset, +    const PcRelativePatchInfo* info_high) { +  return NewPcRelativePatch( +      /* dex_file */ nullptr, boot_image_offset, info_high, &boot_image_method_patches_); +} +  CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewBootImageMethodPatch(      MethodReference target_method,      const PcRelativePatchInfo* info_high) { @@ -1780,6 +1794,34 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo    __ Bind(slow_path->GetExitLabel());  } +void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, +                                                                       GpuRegister temp) { +  uint32_t path_to_root = check->GetBitstringPathToRoot(); +  uint32_t mask = check->GetBitstringMask(); +  DCHECK(IsPowerOfTwo(mask + 1)); +  size_t mask_bits = WhichPowerOf2(mask + 1); + +  if (mask_bits == 16u) { +    // Load only the bitstring part of the status word. +    __ LoadFromOffset( +        kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value()); +    // Compare the bitstring bits using XOR. +    __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); +  } else { +    // /* uint32_t */ temp = temp->status_ +    __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value()); +    // Compare the bitstring bits using XOR. +    if (IsUint<16>(path_to_root)) { +      __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root)); +    } else { +      __ LoadConst32(TMP, path_to_root); +      __ Xor(temp, temp, TMP); +    } +    // Shift out bits that do not contribute to the comparison. +    __ Sll(temp, temp, 32 - mask_bits); +  } +} +  void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {    __ Sync(0);  // only stype 0 is supported  } @@ -2840,7 +2882,13 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations =        new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));  } @@ -2849,7 +2897,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    GpuRegister obj = obj_loc.AsRegister<GpuRegister>(); -  GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); +  Location cls = locations->InAt(1);    Location temp_loc = locations->GetTemp(0);    GpuRegister temp = temp_loc.AsRegister<GpuRegister>();    const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); @@ -2888,7 +2936,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {                                          kWithoutReadBarrier);        // Jump to slow path for throwing the exception or doing a        // more involved array check. -      __ Bnec(temp, cls, slow_path->GetEntryLabel()); +      __ Bnec(temp, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());        break;      } @@ -2914,7 +2962,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {        // exception.        __ Beqzc(temp, slow_path->GetEntryLabel());        // Otherwise, compare the classes. -      __ Bnec(temp, cls, &loop); +      __ Bnec(temp, cls.AsRegister<GpuRegister>(), &loop);        break;      } @@ -2929,7 +2977,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {        // Walk over the class hierarchy to find a match.        Mips64Label loop;        __ Bind(&loop); -      __ Beqc(temp, cls, &done); +      __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);        // /* HeapReference<Class> */ temp = temp->super_class_        GenerateReferenceLoadOneRegister(instruction,                                         temp_loc, @@ -2952,7 +3000,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {                                          maybe_temp2_loc,                                          kWithoutReadBarrier);        // Do an exact check. -      __ Beqc(temp, cls, &done); +      __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);        // Otherwise, we need to check that the object's class is a non-primitive array.        // /* HeapReference<Class> */ temp = temp->component_type_        GenerateReferenceLoadOneRegister(instruction, @@ -3011,7 +3059,21 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {        __ Daddiu(temp, temp, 2 * kHeapReferenceSize);        __ Addiu(TMP, TMP, -2);        // Compare the classes and continue the loop if they do not match. -      __ Bnec(AT, cls, &loop); +      __ Bnec(AT, cls.AsRegister<GpuRegister>(), &loop); +      break; +    } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        temp_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp2_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, temp); +      __ Bnezc(temp, slow_path->GetEntryLabel());        break;      }    } @@ -5515,6 +5577,8 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {      case TypeCheckKind::kInterfaceCheck:        call_kind = LocationSummary::kCallOnSlowPath;        break; +    case TypeCheckKind::kBitstringCheck: +      break;    }    LocationSummary* locations = @@ -5523,7 +5587,13 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {      locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.    }    locations->SetInAt(0, Location::RequiresRegister()); -  locations->SetInAt(1, Location::RequiresRegister()); +  if (type_check_kind == TypeCheckKind::kBitstringCheck) { +    locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant())); +    locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant())); +    locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant())); +  } else { +    locations->SetInAt(1, Location::RequiresRegister()); +  }    // The output does overlap inputs.    // Note that TypeCheckSlowPathMIPS64 uses this register too.    locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); @@ -5535,7 +5605,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {    LocationSummary* locations = instruction->GetLocations();    Location obj_loc = locations->InAt(0);    GpuRegister obj = obj_loc.AsRegister<GpuRegister>(); -  GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>(); +  Location cls = locations->InAt(1);    Location out_loc = locations->Out();    GpuRegister out = out_loc.AsRegister<GpuRegister>();    const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); @@ -5567,7 +5637,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {                                          maybe_temp_loc,                                          read_barrier_option);        // Classes must be equal for the instanceof to succeed. -      __ Xor(out, out, cls); +      __ Xor(out, out, cls.AsRegister<GpuRegister>());        __ Sltiu(out, out, 1);        break;      } @@ -5594,7 +5664,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {                                         read_barrier_option);        // If `out` is null, we use it for the result, and jump to `done`.        __ Beqzc(out, &done); -      __ Bnec(out, cls, &loop); +      __ Bnec(out, cls.AsRegister<GpuRegister>(), &loop);        __ LoadConst32(out, 1);        break;      } @@ -5612,7 +5682,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {        // Walk over the class hierarchy to find a match.        Mips64Label loop, success;        __ Bind(&loop); -      __ Beqc(out, cls, &success); +      __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);        // /* HeapReference<Class> */ out = out->super_class_        GenerateReferenceLoadOneRegister(instruction,                                         out_loc, @@ -5639,7 +5709,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {                                          read_barrier_option);        // Do an exact check.        Mips64Label success; -      __ Beqc(out, cls, &success); +      __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);        // Otherwise, we need to check that the object's class is a non-primitive array.        // /* HeapReference<Class> */ out = out->component_type_        GenerateReferenceLoadOneRegister(instruction, @@ -5671,7 +5741,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {        slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(            instruction, /* is_fatal */ false);        codegen_->AddSlowPath(slow_path); -      __ Bnec(out, cls, slow_path->GetEntryLabel()); +      __ Bnec(out, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());        __ LoadConst32(out, 1);        break;      } @@ -5703,6 +5773,20 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {        __ Bc(slow_path->GetEntryLabel());        break;      } + +    case TypeCheckKind::kBitstringCheck: { +      // /* HeapReference<Class> */ temp = obj->klass_ +      GenerateReferenceLoadTwoRegisters(instruction, +                                        out_loc, +                                        obj_loc, +                                        class_offset, +                                        maybe_temp_loc, +                                        kWithoutReadBarrier); + +      GenerateBitstringTypeCheckCompare(instruction, out); +      __ Sltiu(out, out, 1); +      break; +    }    }    __ Bind(&done); @@ -5839,7 +5923,7 @@ HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(    bool fallback_load = false;    switch (desired_string_load_kind) {      case HLoadString::LoadKind::kBootImageLinkTimePcRelative: -    case HLoadString::LoadKind::kBootImageInternTable: +    case HLoadString::LoadKind::kBootImageRelRo:      case HLoadString::LoadKind::kBssEntry:        DCHECK(!Runtime::Current()->UseJitCompilation());        break; @@ -5866,7 +5950,7 @@ HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind(      case HLoadClass::LoadKind::kReferrersClass:        break;      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: -    case HLoadClass::LoadKind::kBootImageClassTable: +    case HLoadClass::LoadKind::kBootImageRelRo:      case HLoadClass::LoadKind::kBssEntry:        DCHECK(!Runtime::Current()->UseJitCompilation());        break; @@ -5926,6 +6010,15 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(                       kLoadDoubleword,                       DeduplicateUint64Literal(invoke->GetMethodAddress()));        break; +    case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: { +      uint32_t boot_image_offset = GetBootImageOffset(invoke); +      PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset); +      PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high); +      EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low); +      // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load. +      __ Lwu(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); +      break; +    }      case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {        PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(            MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex())); @@ -6113,20 +6206,15 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S                       codegen_->DeduplicateBootImageAddressLiteral(address));        break;      } -    case HLoadClass::LoadKind::kBootImageClassTable: { +    case HLoadClass::LoadKind::kBootImageRelRo: {        DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); +      uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls);        CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = -          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); +          codegen_->NewBootImageRelRoPatch(boot_image_offset);        CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = -          codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high); +          codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);        codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);        __ Lwu(out, AT, /* placeholder */ 0x5678); -      // Extract the reference from the slot data, i.e. clear the hash bits. -      int32_t masked_hash = ClassTable::TableSlot::MaskHash( -          ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex()))); -      if (masked_hash != 0) { -        __ Daddiu(out, out, -masked_hash); -      }        break;      }      case HLoadClass::LoadKind::kBssEntry: { @@ -6248,12 +6336,13 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA                       codegen_->DeduplicateBootImageAddressLiteral(address));        return;      } -    case HLoadString::LoadKind::kBootImageInternTable: { +    case HLoadString::LoadKind::kBootImageRelRo: {        DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); +      uint32_t boot_image_offset = codegen_->GetBootImageOffset(load);        CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = -          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex()); +          codegen_->NewBootImageRelRoPatch(boot_image_offset);        CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = -          codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high); +          codegen_->NewBootImageRelRoPatch(boot_image_offset, info_high);        codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);        __ Lwu(out, AT, /* placeholder */ 0x5678);        return; @@ -6665,6 +6754,236 @@ void InstructionCodeGeneratorMIPS64::VisitRem(HRem* instruction) {    }  } +static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) { +  LocationSummary* locations = new (allocator) LocationSummary(minmax); +  switch (minmax->GetResultType()) { +    case DataType::Type::kInt32: +    case DataType::Type::kInt64: +      locations->SetInAt(0, Location::RequiresRegister()); +      locations->SetInAt(1, Location::RequiresRegister()); +      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +      break; +    case DataType::Type::kFloat32: +    case DataType::Type::kFloat64: +      locations->SetInAt(0, Location::RequiresFpuRegister()); +      locations->SetInAt(1, Location::RequiresFpuRegister()); +      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); +      break; +    default: +      LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType(); +  } +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMaxInt(LocationSummary* locations, bool is_min) { +  GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); +  GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>(); +  GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + +  if (lhs == rhs) { +    if (out != lhs) { +      __ Move(out, lhs); +    } +  } else { +    // Some architectures, such as ARM and MIPS (prior to r6), have a +    // conditional move instruction which only changes the target +    // (output) register if the condition is true (MIPS prior to r6 had +    // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always +    // change the target (output) register.  If the condition is true the +    // output register gets the contents of the "rs" register; otherwise, +    // the output register is set to zero. One consequence of this is +    // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 +    // needs to use a pair of SELEQZ/SELNEZ instructions.  After +    // executing this pair of instructions one of the output registers +    // from the pair will necessarily contain zero. Then the code ORs the +    // output registers from the SELEQZ/SELNEZ instructions to get the +    // final result. +    // +    // The initial test to see if the output register is same as the +    // first input register is needed to make sure that value in the +    // first input register isn't clobbered before we've finished +    // computing the output value. The logic in the corresponding else +    // clause performs the same task but makes sure the second input +    // register isn't clobbered in the event that it's the same register +    // as the output register; the else clause also handles the case +    // where the output register is distinct from both the first, and the +    // second input registers. +    if (out == lhs) { +      __ Slt(AT, rhs, lhs); +      if (is_min) { +        __ Seleqz(out, lhs, AT); +        __ Selnez(AT, rhs, AT); +      } else { +        __ Selnez(out, lhs, AT); +        __ Seleqz(AT, rhs, AT); +      } +    } else { +      __ Slt(AT, lhs, rhs); +      if (is_min) { +        __ Seleqz(out, rhs, AT); +        __ Selnez(AT, lhs, AT); +      } else { +        __ Selnez(out, rhs, AT); +        __ Seleqz(AT, lhs, AT); +      } +    } +    __ Or(out, out, AT); +  } +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMaxFP(LocationSummary* locations, +                                                      bool is_min, +                                                      DataType::Type type) { +  FpuRegister a = locations->InAt(0).AsFpuRegister<FpuRegister>(); +  FpuRegister b = locations->InAt(1).AsFpuRegister<FpuRegister>(); +  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); + +  Mips64Label noNaNs; +  Mips64Label done; +  FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + +  // When Java computes min/max it prefers a NaN to a number; the +  // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of +  // the inputs is a NaN and the other is a valid number, the MIPS +  // instruction will return the number; Java wants the NaN value +  // returned. This is why there is extra logic preceding the use of +  // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a +  // NaN, return the NaN, otherwise return the min/max. +  if (type == DataType::Type::kFloat64) { +    __ CmpUnD(FTMP, a, b); +    __ Bc1eqz(FTMP, &noNaNs); + +    // One of the inputs is a NaN +    __ CmpEqD(ftmp, a, a); +    // If a == a then b is the NaN, otherwise a is the NaN. +    __ SelD(ftmp, a, b); + +    if (ftmp != out) { +      __ MovD(out, ftmp); +    } + +    __ Bc(&done); + +    __ Bind(&noNaNs); + +    if (is_min) { +      __ MinD(out, a, b); +    } else { +      __ MaxD(out, a, b); +    } +  } else { +    DCHECK_EQ(type, DataType::Type::kFloat32); +    __ CmpUnS(FTMP, a, b); +    __ Bc1eqz(FTMP, &noNaNs); + +    // One of the inputs is a NaN +    __ CmpEqS(ftmp, a, a); +    // If a == a then b is the NaN, otherwise a is the NaN. +    __ SelS(ftmp, a, b); + +    if (ftmp != out) { +      __ MovS(out, ftmp); +    } + +    __ Bc(&done); + +    __ Bind(&noNaNs); + +    if (is_min) { +      __ MinS(out, a, b); +    } else { +      __ MaxS(out, a, b); +    } +  } + +  __ Bind(&done); +} + +void InstructionCodeGeneratorMIPS64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) { +  DataType::Type type = minmax->GetResultType(); +  switch (type) { +    case DataType::Type::kInt32: +    case DataType::Type::kInt64: +      GenerateMinMaxInt(minmax->GetLocations(), is_min); +      break; +    case DataType::Type::kFloat32: +    case DataType::Type::kFloat64: +      GenerateMinMaxFP(minmax->GetLocations(), is_min, type); +      break; +    default: +      LOG(FATAL) << "Unexpected type for HMinMax " << type; +  } +} + +void LocationsBuilderMIPS64::VisitMin(HMin* min) { +  CreateMinMaxLocations(GetGraph()->GetAllocator(), min); +} + +void InstructionCodeGeneratorMIPS64::VisitMin(HMin* min) { +  GenerateMinMax(min, /*is_min*/ true); +} + +void LocationsBuilderMIPS64::VisitMax(HMax* max) { +  CreateMinMaxLocations(GetGraph()->GetAllocator(), max); +} + +void InstructionCodeGeneratorMIPS64::VisitMax(HMax* max) { +  GenerateMinMax(max, /*is_min*/ false); +} + +void LocationsBuilderMIPS64::VisitAbs(HAbs* abs) { +  LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs); +  switch (abs->GetResultType()) { +    case DataType::Type::kInt32: +    case DataType::Type::kInt64: +      locations->SetInAt(0, Location::RequiresRegister()); +      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +      break; +    case DataType::Type::kFloat32: +    case DataType::Type::kFloat64: +      locations->SetInAt(0, Location::RequiresFpuRegister()); +      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); +      break; +    default: +      LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); +  } +} + +void InstructionCodeGeneratorMIPS64::VisitAbs(HAbs* abs) { +  LocationSummary* locations = abs->GetLocations(); +  switch (abs->GetResultType()) { +    case DataType::Type::kInt32: { +      GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>(); +      GpuRegister out = locations->Out().AsRegister<GpuRegister>(); +      __ Sra(AT, in, 31); +      __ Xor(out, in, AT); +      __ Subu(out, out, AT); +      break; +    } +    case DataType::Type::kInt64: { +      GpuRegister in  = locations->InAt(0).AsRegister<GpuRegister>(); +      GpuRegister out = locations->Out().AsRegister<GpuRegister>(); +      __ Dsra32(AT, in, 31); +      __ Xor(out, in, AT); +      __ Dsubu(out, out, AT); +      break; +    } +    case DataType::Type::kFloat32: { +      FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>(); +      FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); +      __ AbsS(out, in); +      break; +    } +    case DataType::Type::kFloat64: { +      FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>(); +      FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); +      __ AbsD(out, in); +      break; +    } +    default: +      LOG(FATAL) << "Unexpected abs type " << abs->GetResultType(); +  } +} +  void LocationsBuilderMIPS64::VisitConstructorFence(HConstructorFence* constructor_fence) {    constructor_fence->SetLocations(nullptr);  }  | 
