| Index: src/arm/simulator-arm.cc
|
| diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
|
| index 4c681ae764516170d68f6c0f0642e6e43e4d5d59..c972b42a7393b1cc391e54a0e910d2104873a43b 100644
|
| --- a/src/arm/simulator-arm.cc
|
| +++ b/src/arm/simulator-arm.cc
|
| @@ -1309,6 +1309,33 @@ bool Simulator::OverflowFrom(int32_t alu_out,
|
|
|
|
|
| // Support for VFP comparisons.
|
| +void Simulator::Compute_FPSCR_Flags(float val1, float val2) {
|
| + if (std::isnan(val1) || std::isnan(val2)) {
|
| + n_flag_FPSCR_ = false;
|
| + z_flag_FPSCR_ = false;
|
| + c_flag_FPSCR_ = true;
|
| + v_flag_FPSCR_ = true;
|
| + // All non-NaN cases.
|
| + } else if (val1 == val2) {
|
| + n_flag_FPSCR_ = false;
|
| + z_flag_FPSCR_ = true;
|
| + c_flag_FPSCR_ = true;
|
| + v_flag_FPSCR_ = false;
|
| + } else if (val1 < val2) {
|
| + n_flag_FPSCR_ = true;
|
| + z_flag_FPSCR_ = false;
|
| + c_flag_FPSCR_ = false;
|
| + v_flag_FPSCR_ = false;
|
| + } else {
|
| + // Case when (val1 > val2).
|
| + n_flag_FPSCR_ = false;
|
| + z_flag_FPSCR_ = false;
|
| + c_flag_FPSCR_ = true;
|
| + v_flag_FPSCR_ = false;
|
| + }
|
| +}
|
| +
|
| +
|
| void Simulator::Compute_FPSCR_Flags(double val1, double val2) {
|
| if (std::isnan(val1) || std::isnan(val2)) {
|
| n_flag_FPSCR_ = false;
|
| @@ -1914,6 +1941,17 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
|
| }
|
|
|
|
|
| +float Simulator::canonicalizeNaN(float value) {
|
| + // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation
|
| + // choices" of the ARM Reference Manual.
|
| + const uint32_t kDefaultNaN = 0x7FC00000u;
|
| + if (FPSCR_default_NaN_mode_ && std::isnan(value)) {
|
| + value = bit_cast<float>(kDefaultNaN);
|
| + }
|
| + return value;
|
| +}
|
| +
|
| +
|
| double Simulator::canonicalizeNaN(double value) {
|
| // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation
|
| // choices" of the ARM Reference Manual.
|
| @@ -3009,18 +3047,30 @@ void Simulator::DecodeType7(Instruction* instr) {
|
| // vcvt: Sd = Dm
|
| // vcvt.f64.s32 Dd, Dd, #<fbits>
|
| // Dd = vabs(Dm)
|
| +// Sd = vabs(Sm)
|
| // Dd = vneg(Dm)
|
| +// Sd = vneg(Sm)
|
| // Dd = vadd(Dn, Dm)
|
| +// Sd = vadd(Sn, Sm)
|
| // Dd = vsub(Dn, Dm)
|
| +// Sd = vsub(Sn, Sm)
|
| // Dd = vmul(Dn, Dm)
|
| +// Sd = vmul(Sn, Sm)
|
| // Dd = vdiv(Dn, Dm)
|
| +// Sd = vdiv(Sn, Sm)
|
| // vcmp(Dd, Dm)
|
| -// vmrs
|
| +// vcmp(Sd, Sm)
|
| // Dd = vsqrt(Dm)
|
| +// Sd = vsqrt(Sm)
|
| +// vmrs
|
| void Simulator::DecodeTypeVFP(Instruction* instr) {
|
| DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) );
|
| DCHECK(instr->Bits(11, 9) == 0x5);
|
|
|
| + // Obtain single precision register codes.
|
| + int m = instr->VFPMRegValue(kSinglePrecision);
|
| + int d = instr->VFPDRegValue(kSinglePrecision);
|
| + int n = instr->VFPNRegValue(kSinglePrecision);
|
| // Obtain double precision register codes.
|
| int vm = instr->VFPMRegValue(kDoublePrecision);
|
| int vd = instr->VFPDRegValue(kDoublePrecision);
|
| @@ -3032,28 +3082,38 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
|
| if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) {
|
| // vmov register to register.
|
| if (instr->SzValue() == 0x1) {
|
| - int m = instr->VFPMRegValue(kDoublePrecision);
|
| - int d = instr->VFPDRegValue(kDoublePrecision);
|
| uint32_t data[2];
|
| - get_d_register(m, data);
|
| - set_d_register(d, data);
|
| + get_d_register(vm, data);
|
| + set_d_register(vd, data);
|
| } else {
|
| - int m = instr->VFPMRegValue(kSinglePrecision);
|
| - int d = instr->VFPDRegValue(kSinglePrecision);
|
| - set_s_register_from_float(d, get_float_from_s_register(m));
|
| + set_s_register(d, get_s_register(m));
|
| }
|
| } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) {
|
| // vabs
|
| - double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = std::fabs(dm_value);
|
| - dd_value = canonicalizeNaN(dd_value);
|
| - set_d_register_from_double(vd, dd_value);
|
| + if (instr->SzValue() == 0x1) {
|
| + double dm_value = get_double_from_d_register(vm);
|
| + double dd_value = std::fabs(dm_value);
|
| + dd_value = canonicalizeNaN(dd_value);
|
| + set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = std::fabs(sm_value);
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| + }
|
| } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) {
|
| // vneg
|
| - double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = -dm_value;
|
| - dd_value = canonicalizeNaN(dd_value);
|
| - set_d_register_from_double(vd, dd_value);
|
| + if (instr->SzValue() == 0x1) {
|
| + double dm_value = get_double_from_d_register(vm);
|
| + double dd_value = -dm_value;
|
| + dd_value = canonicalizeNaN(dd_value);
|
| + set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = -sm_value;
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| + }
|
| } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) {
|
| DecodeVCVTBetweenDoubleAndSingle(instr);
|
| } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) {
|
| @@ -3073,10 +3133,17 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
|
| DecodeVCMP(instr);
|
| } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) {
|
| // vsqrt
|
| - double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = fast_sqrt(dm_value);
|
| - dd_value = canonicalizeNaN(dd_value);
|
| - set_d_register_from_double(vd, dd_value);
|
| + if (instr->SzValue() == 0x1) {
|
| + double dm_value = get_double_from_d_register(vm);
|
| + double dd_value = fast_sqrt(dm_value);
|
| + dd_value = canonicalizeNaN(dd_value);
|
| + set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = fast_sqrt(sm_value);
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| + }
|
| } else if (instr->Opc3Value() == 0x0) {
|
| // vmov immediate.
|
| if (instr->SzValue() == 0x1) {
|
| @@ -3094,72 +3161,103 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
|
| UNREACHABLE(); // Not used by V8.
|
| }
|
| } else if (instr->Opc1Value() == 0x3) {
|
| - if (instr->SzValue() != 0x1) {
|
| - UNREACHABLE(); // Not used by V8.
|
| - }
|
| -
|
| if (instr->Opc3Value() & 0x1) {
|
| // vsub
|
| - double dn_value = get_double_from_d_register(vn);
|
| - double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = dn_value - dm_value;
|
| - dd_value = canonicalizeNaN(dd_value);
|
| - set_d_register_from_double(vd, dd_value);
|
| + if (instr->SzValue() == 0x1) {
|
| + double dn_value = get_double_from_d_register(vn);
|
| + double dm_value = get_double_from_d_register(vm);
|
| + double dd_value = dn_value - dm_value;
|
| + dd_value = canonicalizeNaN(dd_value);
|
| + set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sn_value = get_float_from_s_register(n);
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = sn_value - sm_value;
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| + }
|
| } else {
|
| // vadd
|
| + if (instr->SzValue() == 0x1) {
|
| + double dn_value = get_double_from_d_register(vn);
|
| + double dm_value = get_double_from_d_register(vm);
|
| + double dd_value = dn_value + dm_value;
|
| + dd_value = canonicalizeNaN(dd_value);
|
| + set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sn_value = get_float_from_s_register(n);
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = sn_value + sm_value;
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| + }
|
| + }
|
| + } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) {
|
| + // vmul
|
| + if (instr->SzValue() == 0x1) {
|
| double dn_value = get_double_from_d_register(vn);
|
| double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = dn_value + dm_value;
|
| + double dd_value = dn_value * dm_value;
|
| dd_value = canonicalizeNaN(dd_value);
|
| set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sn_value = get_float_from_s_register(n);
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = sn_value * sm_value;
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| }
|
| - } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) {
|
| - // vmul
|
| - if (instr->SzValue() != 0x1) {
|
| - UNREACHABLE(); // Not used by V8.
|
| - }
|
| -
|
| - double dn_value = get_double_from_d_register(vn);
|
| - double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = dn_value * dm_value;
|
| - dd_value = canonicalizeNaN(dd_value);
|
| - set_d_register_from_double(vd, dd_value);
|
| } else if ((instr->Opc1Value() == 0x0)) {
|
| // vmla, vmls
|
| const bool is_vmls = (instr->Opc3Value() & 0x1);
|
| -
|
| - if (instr->SzValue() != 0x1) {
|
| - UNREACHABLE(); // Not used by V8.
|
| - }
|
| -
|
| - const double dd_val = get_double_from_d_register(vd);
|
| - const double dn_val = get_double_from_d_register(vn);
|
| - const double dm_val = get_double_from_d_register(vm);
|
| -
|
| - // Note: we do the mul and add/sub in separate steps to avoid getting a
|
| - // result with too high precision.
|
| - set_d_register_from_double(vd, dn_val * dm_val);
|
| - if (is_vmls) {
|
| - set_d_register_from_double(
|
| - vd,
|
| - canonicalizeNaN(dd_val - get_double_from_d_register(vd)));
|
| + if (instr->SzValue() == 0x1) {
|
| + const double dd_val = get_double_from_d_register(vd);
|
| + const double dn_val = get_double_from_d_register(vn);
|
| + const double dm_val = get_double_from_d_register(vm);
|
| +
|
| + // Note: we do the mul and add/sub in separate steps to avoid getting a
|
| + // result with too high precision.
|
| + set_d_register_from_double(vd, dn_val * dm_val);
|
| + if (is_vmls) {
|
| + set_d_register_from_double(
|
| + vd, canonicalizeNaN(dd_val - get_double_from_d_register(vd)));
|
| + } else {
|
| + set_d_register_from_double(
|
| + vd, canonicalizeNaN(dd_val + get_double_from_d_register(vd)));
|
| + }
|
| } else {
|
| - set_d_register_from_double(
|
| - vd,
|
| - canonicalizeNaN(dd_val + get_double_from_d_register(vd)));
|
| + const float sd_val = get_float_from_s_register(d);
|
| + const float sn_val = get_float_from_s_register(n);
|
| + const float sm_val = get_float_from_s_register(m);
|
| +
|
| + // Note: we do the mul and add/sub in separate steps to avoid getting a
|
| + // result with too high precision.
|
| + set_s_register_from_float(d, sn_val * sm_val);
|
| + if (is_vmls) {
|
| + set_s_register_from_float(
|
| + d, canonicalizeNaN(sd_val - get_float_from_s_register(d)));
|
| + } else {
|
| + set_s_register_from_float(
|
| + d, canonicalizeNaN(sd_val + get_float_from_s_register(d)));
|
| + }
|
| }
|
| } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) {
|
| // vdiv
|
| - if (instr->SzValue() != 0x1) {
|
| - UNREACHABLE(); // Not used by V8.
|
| + if (instr->SzValue() == 0x1) {
|
| + double dn_value = get_double_from_d_register(vn);
|
| + double dm_value = get_double_from_d_register(vm);
|
| + double dd_value = dn_value / dm_value;
|
| + div_zero_vfp_flag_ = (dm_value == 0);
|
| + dd_value = canonicalizeNaN(dd_value);
|
| + set_d_register_from_double(vd, dd_value);
|
| + } else {
|
| + float sn_value = get_float_from_s_register(n);
|
| + float sm_value = get_float_from_s_register(m);
|
| + float sd_value = sn_value / sm_value;
|
| + div_zero_vfp_flag_ = (sm_value == 0);
|
| + sd_value = canonicalizeNaN(sd_value);
|
| + set_s_register_from_float(d, sd_value);
|
| }
|
| -
|
| - double dn_value = get_double_from_d_register(vn);
|
| - double dm_value = get_double_from_d_register(vm);
|
| - double dd_value = dn_value / dm_value;
|
| - div_zero_vfp_flag_ = (dm_value == 0);
|
| - dd_value = canonicalizeNaN(dd_value);
|
| - set_d_register_from_double(vd, dd_value);
|
| } else {
|
| UNIMPLEMENTED(); // Not used by V8.
|
| }
|
| @@ -3264,7 +3362,7 @@ void Simulator::DecodeVCMP(Instruction* instr) {
|
| // Comparison.
|
|
|
| VFPRegPrecision precision = kSinglePrecision;
|
| - if (instr->SzValue() == 1) {
|
| + if (instr->SzValue() == 0x1) {
|
| precision = kDoublePrecision;
|
| }
|
|
|
| @@ -3290,7 +3388,20 @@ void Simulator::DecodeVCMP(Instruction* instr) {
|
|
|
| Compute_FPSCR_Flags(dd_value, dm_value);
|
| } else {
|
| - UNIMPLEMENTED(); // Not used by V8.
|
| + float sd_value = get_float_from_s_register(d);
|
| + float sm_value = 0.0;
|
| + if (instr->Opc2Value() == 0x4) {
|
| + sm_value = get_float_from_s_register(m);
|
| + }
|
| +
|
| + // Raise exceptions for quiet NaNs if necessary.
|
| + if (instr->Bit(7) == 1) {
|
| + if (std::isnan(sd_value)) {
|
| + inv_op_vfp_flag_ = true;
|
| + }
|
| + }
|
| +
|
| + Compute_FPSCR_Flags(sd_value, sm_value);
|
| }
|
| }
|
|
|
|
|