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); |
} |
} |