void InstructionCodeGeneratorMIPS::VisitTypeConversion()

in compiler/optimizing/code_generator_mips.cc [4761:5028]


void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversion) {
  LocationSummary* locations = conversion->GetLocations();
  Primitive::Type result_type = conversion->GetResultType();
  Primitive::Type input_type = conversion->GetInputType();
  bool has_sign_extension = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
  bool fpu_32bit = codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint();

  DCHECK_NE(input_type, result_type);

  if (result_type == Primitive::kPrimLong && Primitive::IsIntegralType(input_type)) {
    Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
    Register dst_low = locations->Out().AsRegisterPairLow<Register>();
    Register src = locations->InAt(0).AsRegister<Register>();

    __ Move(dst_low, src);
    __ Sra(dst_high, src, 31);
  } else if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
    Register dst = locations->Out().AsRegister<Register>();
    Register src = (input_type == Primitive::kPrimLong)
        ? locations->InAt(0).AsRegisterPairLow<Register>()
        : locations->InAt(0).AsRegister<Register>();

    switch (result_type) {
      case Primitive::kPrimChar:
        __ Andi(dst, src, 0xFFFF);
        break;
      case Primitive::kPrimByte:
        if (has_sign_extension) {
          __ Seb(dst, src);
        } else {
          __ Sll(dst, src, 24);
          __ Sra(dst, dst, 24);
        }
        break;
      case Primitive::kPrimShort:
        if (has_sign_extension) {
          __ Seh(dst, src);
        } else {
          __ Sll(dst, src, 16);
          __ Sra(dst, dst, 16);
        }
        break;
      case Primitive::kPrimInt:
        __ Move(dst, src);
        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) {
      if (isR6) {
        // cvt.s.l/cvt.d.l requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
        // architecture on top of MIPS64R6, which has FR=1, and therefore can use the instruction.
        Register src_high = locations->InAt(0).AsRegisterPairHigh<Register>();
        Register src_low = locations->InAt(0).AsRegisterPairLow<Register>();
        FRegister dst = locations->Out().AsFpuRegister<FRegister>();
        __ Mtc1(src_low, FTMP);
        __ Mthc1(src_high, FTMP);
        if (result_type == Primitive::kPrimFloat) {
          __ Cvtsl(dst, FTMP);
        } else {
          __ Cvtdl(dst, FTMP);
        }
      } else {
        int32_t entry_offset = (result_type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pL2f)
                                                                      : QUICK_ENTRY_POINT(pL2d);
        bool direct = (result_type == Primitive::kPrimFloat) ? IsDirectEntrypoint(kQuickL2f)
                                                             : IsDirectEntrypoint(kQuickL2d);
        codegen_->InvokeRuntime(entry_offset,
                                conversion,
                                conversion->GetDexPc(),
                                nullptr,
                                direct);
        if (result_type == Primitive::kPrimFloat) {
          CheckEntrypointTypes<kQuickL2f, float, int64_t>();
        } else {
          CheckEntrypointTypes<kQuickL2d, double, int64_t>();
        }
      }
    } else {
      Register src = locations->InAt(0).AsRegister<Register>();
      FRegister dst = locations->Out().AsFpuRegister<FRegister>();
      __ Mtc1(src, FTMP);
      if (result_type == Primitive::kPrimFloat) {
        __ Cvtsw(dst, FTMP);
      } else {
        __ Cvtdw(dst, FTMP);
      }
    }
  } else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) {
    CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong);
    if (result_type == Primitive::kPrimLong) {
      if (isR6) {
        // trunc.l.s/trunc.l.d requires MIPSR2+ with FR=1. MIPS32R6 is implemented as a secondary
        // architecture on top of MIPS64R6, which has FR=1, and therefore can use the instruction.
        FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
        Register dst_high = locations->Out().AsRegisterPairHigh<Register>();
        Register dst_low = locations->Out().AsRegisterPairLow<Register>();
        MipsLabel truncate;
        MipsLabel done;

        // When NAN2008=0 (R2 and before), the truncate instruction produces the maximum positive
        // value when the input is either a NaN or is outside of the range of the output type
        // after the truncation. IOW, the three special cases (NaN, too small, too big) produce
        // the same result.
        //
        // When NAN2008=1 (R6), the truncate instruction caps the output at the minimum/maximum
        // value of the output type if the input is outside of the range after the truncation or
        // produces 0 when the input is a NaN. IOW, the three special cases produce three distinct
        // results. This matches the desired float/double-to-int/long conversion exactly.
        //
        // So, NAN2008 affects handling of negative values and NaNs by the truncate instruction.
        //
        // The following code supports both NAN2008=0 and NAN2008=1 behaviors of the truncate
        // instruction, the reason being that the emulator implements NAN2008=0 on MIPS64R6,
        // even though it must be NAN2008=1 on R6.
        //
        // The code takes care of the different behaviors by first comparing the input to the
        // minimum output value (-2**-63 for truncating to long, -2**-31 for truncating to int).
        // If the input is greater than or equal to the minimum, it procedes to the truncate
        // instruction, which will handle such an input the same way irrespective of NAN2008.
        // Otherwise the input is compared to itself to determine whether it is a NaN or not
        // in order to return either zero or the minimum value.
        //
        // TODO: simplify this when the emulator correctly implements NAN2008=1 behavior of the
        // truncate instruction for MIPS64R6.
        if (input_type == Primitive::kPrimFloat) {
          uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int64_t>::min());
          __ LoadConst32(TMP, min_val);
          __ Mtc1(TMP, FTMP);
          __ CmpLeS(FTMP, FTMP, src);
        } else {
          uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int64_t>::min());
          __ LoadConst32(TMP, High32Bits(min_val));
          __ Mtc1(ZERO, FTMP);
          __ Mthc1(TMP, FTMP);
          __ CmpLeD(FTMP, FTMP, src);
        }

        __ Bc1nez(FTMP, &truncate);

        if (input_type == Primitive::kPrimFloat) {
          __ CmpEqS(FTMP, src, src);
        } else {
          __ CmpEqD(FTMP, src, src);
        }
        __ Move(dst_low, ZERO);
        __ LoadConst32(dst_high, std::numeric_limits<int32_t>::min());
        __ Mfc1(TMP, FTMP);
        __ And(dst_high, dst_high, TMP);

        __ B(&done);

        __ Bind(&truncate);

        if (input_type == Primitive::kPrimFloat) {
          __ TruncLS(FTMP, src);
        } else {
          __ TruncLD(FTMP, src);
        }
        __ Mfc1(dst_low, FTMP);
        __ Mfhc1(dst_high, FTMP);

        __ Bind(&done);
      } else {
        int32_t entry_offset = (input_type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pF2l)
                                                                     : QUICK_ENTRY_POINT(pD2l);
        bool direct = (result_type == Primitive::kPrimFloat) ? IsDirectEntrypoint(kQuickF2l)
                                                             : IsDirectEntrypoint(kQuickD2l);
        codegen_->InvokeRuntime(entry_offset, conversion, conversion->GetDexPc(), nullptr, direct);
        if (input_type == Primitive::kPrimFloat) {
          CheckEntrypointTypes<kQuickF2l, int64_t, float>();
        } else {
          CheckEntrypointTypes<kQuickD2l, int64_t, double>();
        }
      }
    } else {
      FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
      Register dst = locations->Out().AsRegister<Register>();
      MipsLabel truncate;
      MipsLabel done;

      // The following code supports both NAN2008=0 and NAN2008=1 behaviors of the truncate
      // instruction, the reason being that the emulator implements NAN2008=0 on MIPS64R6,
      // even though it must be NAN2008=1 on R6.
      //
      // For details see the large comment above for the truncation of float/double to long on R6.
      //
      // TODO: simplify this when the emulator correctly implements NAN2008=1 behavior of the
      // truncate instruction for MIPS64R6.
      if (input_type == Primitive::kPrimFloat) {
        uint32_t min_val = bit_cast<uint32_t, float>(std::numeric_limits<int32_t>::min());
        __ LoadConst32(TMP, min_val);
        __ Mtc1(TMP, FTMP);
      } else {
        uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
        __ LoadConst32(TMP, High32Bits(min_val));
        __ Mtc1(ZERO, FTMP);
        if (fpu_32bit) {
          __ Mtc1(TMP, static_cast<FRegister>(FTMP + 1));
        } else {
          __ Mthc1(TMP, FTMP);
        }
      }

      if (isR6) {
        if (input_type == Primitive::kPrimFloat) {
          __ CmpLeS(FTMP, FTMP, src);
        } else {
          __ CmpLeD(FTMP, FTMP, src);
        }
        __ Bc1nez(FTMP, &truncate);

        if (input_type == Primitive::kPrimFloat) {
          __ CmpEqS(FTMP, src, src);
        } else {
          __ CmpEqD(FTMP, src, src);
        }
        __ LoadConst32(dst, std::numeric_limits<int32_t>::min());
        __ Mfc1(TMP, FTMP);
        __ And(dst, dst, TMP);
      } else {
        if (input_type == Primitive::kPrimFloat) {
          __ ColeS(0, FTMP, src);
        } else {
          __ ColeD(0, FTMP, src);
        }
        __ Bc1t(0, &truncate);

        if (input_type == Primitive::kPrimFloat) {
          __ CeqS(0, src, src);
        } else {
          __ CeqD(0, src, src);
        }
        __ LoadConst32(dst, std::numeric_limits<int32_t>::min());
        __ Movf(dst, ZERO, 0);
      }

      __ B(&done);

      __ Bind(&truncate);

      if (input_type == Primitive::kPrimFloat) {
        __ TruncWS(FTMP, src);
      } else {
        __ TruncWD(FTMP, src);
      }
      __ Mfc1(dst, FTMP);

      __ Bind(&done);
    }
  } else if (Primitive::IsFloatingPointType(result_type) &&
             Primitive::IsFloatingPointType(input_type)) {
    FRegister dst = locations->Out().AsFpuRegister<FRegister>();
    FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
    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;
  }
}