Implements DoubleToI on ARM. Refactor some VFP code at the same time and
fix the simulator behaviour. BUG=none TEST=added to cctest/test-assembler-arm.cc Patch by Rodolph Perfetta from ARM Ltd. Review URL: http://codereview.chromium.org/6368053 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6629 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
3a214b8f50
commit
84de496896
@ -2124,7 +2124,7 @@ static Instr EncodeVCVT(const VFPType dst_type,
|
||||
const int dst_code,
|
||||
const VFPType src_type,
|
||||
const int src_code,
|
||||
Assembler::ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(src_type != dst_type);
|
||||
int D, Vd, M, Vm;
|
||||
@ -2167,7 +2167,7 @@ static Instr EncodeVCVT(const VFPType dst_type,
|
||||
|
||||
void Assembler::vcvt_f64_s32(const DwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(F64, dst.code(), S32, src.code(), mode, cond));
|
||||
@ -2176,7 +2176,7 @@ void Assembler::vcvt_f64_s32(const DwVfpRegister dst,
|
||||
|
||||
void Assembler::vcvt_f32_s32(const SwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(F32, dst.code(), S32, src.code(), mode, cond));
|
||||
@ -2185,7 +2185,7 @@ void Assembler::vcvt_f32_s32(const SwVfpRegister dst,
|
||||
|
||||
void Assembler::vcvt_f64_u32(const DwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(F64, dst.code(), U32, src.code(), mode, cond));
|
||||
@ -2194,7 +2194,7 @@ void Assembler::vcvt_f64_u32(const DwVfpRegister dst,
|
||||
|
||||
void Assembler::vcvt_s32_f64(const SwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(S32, dst.code(), F64, src.code(), mode, cond));
|
||||
@ -2203,7 +2203,7 @@ void Assembler::vcvt_s32_f64(const SwVfpRegister dst,
|
||||
|
||||
void Assembler::vcvt_u32_f64(const SwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(U32, dst.code(), F64, src.code(), mode, cond));
|
||||
@ -2212,7 +2212,7 @@ void Assembler::vcvt_u32_f64(const SwVfpRegister dst,
|
||||
|
||||
void Assembler::vcvt_f64_f32(const DwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(F64, dst.code(), F32, src.code(), mode, cond));
|
||||
@ -2221,7 +2221,7 @@ void Assembler::vcvt_f64_f32(const DwVfpRegister dst,
|
||||
|
||||
void Assembler::vcvt_f32_f64(const SwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
ConversionMode mode,
|
||||
VFPConversionMode mode,
|
||||
const Condition cond) {
|
||||
ASSERT(CpuFeatures::IsEnabled(VFP3));
|
||||
emit(EncodeVCVT(F32, dst.code(), F64, src.code(), mode, cond));
|
||||
|
@ -942,37 +942,33 @@ class Assembler : public Malloced {
|
||||
void vmov(const Register dst,
|
||||
const SwVfpRegister src,
|
||||
const Condition cond = al);
|
||||
enum ConversionMode {
|
||||
FPSCRRounding = 0,
|
||||
RoundToZero = 1
|
||||
};
|
||||
void vcvt_f64_s32(const DwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
void vcvt_f32_s32(const SwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
void vcvt_f64_u32(const DwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
void vcvt_s32_f64(const SwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
void vcvt_u32_f64(const SwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
void vcvt_f64_f32(const DwVfpRegister dst,
|
||||
const SwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
void vcvt_f32_f64(const SwVfpRegister dst,
|
||||
const DwVfpRegister src,
|
||||
ConversionMode mode = RoundToZero,
|
||||
VFPConversionMode mode = kDefaultRoundToZero,
|
||||
const Condition cond = al);
|
||||
|
||||
void vabs(const DwVfpRegister dst,
|
||||
|
@ -380,10 +380,13 @@ enum VFPRegPrecision {
|
||||
|
||||
|
||||
// VFP FPSCR constants.
|
||||
enum VFPConversionMode {
|
||||
kFPSCRRounding = 0,
|
||||
kDefaultRoundToZero = 1
|
||||
};
|
||||
|
||||
static const uint32_t kVFPExceptionMask = 0xf;
|
||||
static const uint32_t kVFPRoundingModeMask = 3 << 22;
|
||||
static const uint32_t kVFPFlushToZeroMask = 1 << 24;
|
||||
static const uint32_t kVFPRoundToMinusInfinityBits = 2 << 22;
|
||||
static const uint32_t kVFPInvalidExceptionBit = 1;
|
||||
|
||||
static const uint32_t kVFPNConditionFlagBit = 1 << 31;
|
||||
@ -393,13 +396,20 @@ static const uint32_t kVFPVConditionFlagBit = 1 << 28;
|
||||
|
||||
|
||||
// VFP rounding modes. See ARM DDI 0406B Page A2-29.
|
||||
enum FPSCRRoundingModes {
|
||||
RN, // Round to Nearest.
|
||||
RP, // Round towards Plus Infinity.
|
||||
RM, // Round towards Minus Infinity.
|
||||
RZ // Round towards zero.
|
||||
enum VFPRoundingMode {
|
||||
RN = 0 << 22, // Round to Nearest.
|
||||
RP = 1 << 22, // Round towards Plus Infinity.
|
||||
RM = 2 << 22, // Round towards Minus Infinity.
|
||||
RZ = 3 << 22, // Round towards zero.
|
||||
|
||||
// Aliases.
|
||||
kRoundToNearest = RN,
|
||||
kRoundToPlusInf = RP,
|
||||
kRoundToMinusInf = RM,
|
||||
kRoundToZero = RZ
|
||||
};
|
||||
|
||||
static const uint32_t kVFPRoundingModeMask = 3 << 22;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Hints.
|
||||
|
@ -1535,7 +1535,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
|
||||
} else {
|
||||
ASSERT(to.IsInteger32());
|
||||
LOperand* value = UseRegister(instr->value());
|
||||
LDoubleToI* res = new LDoubleToI(value);
|
||||
LDoubleToI* res = new LDoubleToI(value, TempRegister());
|
||||
return AssignEnvironment(DefineAsRegister(res));
|
||||
}
|
||||
} else if (from.IsInteger32()) {
|
||||
|
@ -1375,10 +1375,11 @@ class LNumberTagD: public LTemplateInstruction<1, 1, 2> {
|
||||
|
||||
|
||||
// Sometimes truncating conversion from a tagged value to an int32.
|
||||
class LDoubleToI: public LTemplateInstruction<1, 1, 0> {
|
||||
class LDoubleToI: public LTemplateInstruction<1, 1, 1> {
|
||||
public:
|
||||
explicit LDoubleToI(LOperand* value) {
|
||||
explicit LDoubleToI(LOperand* value, LOperand* temp1) {
|
||||
inputs_[0] = value;
|
||||
temps_[0] = temp1;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
|
||||
|
@ -2666,34 +2666,53 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
|
||||
DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
Register prev_fpscr = ToRegister(instr->TempAt(0));
|
||||
SwVfpRegister single_scratch = double_scratch0().low();
|
||||
Register scratch = scratch0();
|
||||
// Truncates a double using a specific rounding mode.
|
||||
// Clears the z flag (ne condition) if an overflow occurs.
|
||||
void LCodeGen::EmitVFPTruncate(VFPRoundingMode rounding_mode,
|
||||
SwVfpRegister result,
|
||||
DwVfpRegister double_input,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
Register prev_fpscr = scratch1;
|
||||
Register scratch = scratch2;
|
||||
|
||||
// Set custom FPCSR:
|
||||
// - Set rounding mode to "Round towards Minus Infinity".
|
||||
// - Set rounding mode.
|
||||
// - Clear vfp cumulative exception flags.
|
||||
// - Make sure Flush-to-zero mode control bit is unset.
|
||||
__ vmrs(prev_fpscr);
|
||||
__ bic(scratch, prev_fpscr,
|
||||
Operand(kVFPExceptionMask | kVFPRoundingModeMask | kVFPFlushToZeroMask));
|
||||
__ orr(scratch, scratch, Operand(kVFPRoundToMinusInfinityBits));
|
||||
__ bic(scratch, prev_fpscr, Operand(kVFPExceptionMask |
|
||||
kVFPRoundingModeMask |
|
||||
kVFPFlushToZeroMask));
|
||||
__ orr(scratch, scratch, Operand(rounding_mode));
|
||||
__ vmsr(scratch);
|
||||
|
||||
// Convert the argument to an integer.
|
||||
__ vcvt_s32_f64(single_scratch,
|
||||
input,
|
||||
Assembler::FPSCRRounding,
|
||||
al);
|
||||
__ vcvt_s32_f64(result,
|
||||
double_input,
|
||||
kFPSCRRounding);
|
||||
|
||||
// Retrieve FPSCR and check for vfp exceptions.
|
||||
// Retrieve FPSCR.
|
||||
__ vmrs(scratch);
|
||||
// Restore FPSCR
|
||||
// Restore FPSCR.
|
||||
__ vmsr(prev_fpscr);
|
||||
// Check for vfp exceptions.
|
||||
__ tst(scratch, Operand(kVFPExceptionMask));
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
|
||||
DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
|
||||
Register result = ToRegister(instr->result());
|
||||
SwVfpRegister single_scratch = double_scratch0().low();
|
||||
Register scratch1 = scratch0();
|
||||
Register scratch2 = ToRegister(instr->TempAt(0));
|
||||
|
||||
EmitVFPTruncate(kRoundToMinusInf,
|
||||
single_scratch,
|
||||
input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
|
||||
// Move the result back to general purpose register r0.
|
||||
@ -2703,8 +2722,8 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
|
||||
Label done;
|
||||
__ cmp(result, Operand(0));
|
||||
__ b(ne, &done);
|
||||
__ vmov(scratch, input.high());
|
||||
__ tst(scratch, Operand(HeapNumber::kSignMask));
|
||||
__ vmov(scratch1, input.high());
|
||||
__ tst(scratch1, Operand(HeapNumber::kSignMask));
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
__ bind(&done);
|
||||
}
|
||||
@ -3338,7 +3357,43 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
|
||||
|
||||
|
||||
void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
|
||||
Abort("DoDoubleToI unimplemented.");
|
||||
LOperand* input = instr->InputAt(0);
|
||||
ASSERT(input->IsDoubleRegister());
|
||||
LOperand* result = instr->result();
|
||||
ASSERT(result->IsRegister());
|
||||
|
||||
DoubleRegister double_input = ToDoubleRegister(input);
|
||||
Register result_reg = ToRegister(result);
|
||||
SwVfpRegister single_scratch = double_scratch0().low();
|
||||
Register scratch1 = scratch0();
|
||||
Register scratch2 = ToRegister(instr->TempAt(0));
|
||||
|
||||
VFPRoundingMode rounding_mode = instr->truncating() ? kRoundToMinusInf
|
||||
: kRoundToNearest;
|
||||
|
||||
|
||||
EmitVFPTruncate(rounding_mode,
|
||||
single_scratch,
|
||||
double_input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
// Deoptimize if we had a vfp invalid exception.
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
// Retrieve the result.
|
||||
__ vmov(result_reg, single_scratch);
|
||||
|
||||
if (instr->truncating() &&
|
||||
instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
|
||||
Label done;
|
||||
__ cmp(result_reg, Operand(0));
|
||||
__ b(ne, &done);
|
||||
// Check for -0.
|
||||
__ vmov(scratch1, double_input.high());
|
||||
__ tst(scratch1, Operand(HeapNumber::kSignMask));
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -219,6 +219,11 @@ class LCodeGen BASE_EMBEDDED {
|
||||
// Specific math operations - used from DoUnaryMathOperation.
|
||||
void EmitIntegerMathAbs(LUnaryMathOperation* instr);
|
||||
void DoMathAbs(LUnaryMathOperation* instr);
|
||||
void EmitVFPTruncate(VFPRoundingMode rounding_mode,
|
||||
SwVfpRegister result,
|
||||
DwVfpRegister double_input,
|
||||
Register scratch1,
|
||||
Register scratch2);
|
||||
void DoMathFloor(LUnaryMathOperation* instr);
|
||||
void DoMathSqrt(LUnaryMathOperation* instr);
|
||||
|
||||
|
@ -2539,7 +2539,7 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
|
||||
(overflow_vfp_flag_ << 2) |
|
||||
(div_zero_vfp_flag_ << 1) |
|
||||
(inv_op_vfp_flag_ << 0) |
|
||||
(FPSCR_rounding_mode_ << 22);
|
||||
(FPSCR_rounding_mode_);
|
||||
set_register(rt, fpscr);
|
||||
}
|
||||
} else if ((instr->VLValue() == 0x0) &&
|
||||
@ -2562,7 +2562,7 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
|
||||
div_zero_vfp_flag_ = (rt_value >> 1) & 1;
|
||||
inv_op_vfp_flag_ = (rt_value >> 0) & 1;
|
||||
FPSCR_rounding_mode_ =
|
||||
static_cast<FPSCRRoundingModes>((rt_value >> 22) & 3);
|
||||
static_cast<VFPRoundingMode>((rt_value) & kVFPRoundingModeMask);
|
||||
}
|
||||
} else {
|
||||
UNIMPLEMENTED(); // Not used by V8.
|
||||
@ -2651,86 +2651,134 @@ void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) {
|
||||
}
|
||||
}
|
||||
|
||||
bool get_inv_op_vfp_flag(VFPRoundingMode mode,
|
||||
double val,
|
||||
bool unsigned_) {
|
||||
ASSERT((mode == RN) || (mode == RM) || (mode == RZ));
|
||||
double max_uint = static_cast<double>(0xffffffffu);
|
||||
double max_int = static_cast<double>(kMaxInt);
|
||||
double min_int = static_cast<double>(kMinInt);
|
||||
|
||||
// Check for NaN.
|
||||
if (val != val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for overflow. This code works because 32bit integers can be
|
||||
// exactly represented by ieee-754 64bit floating-point values.
|
||||
switch (mode) {
|
||||
case RN:
|
||||
return unsigned_ ? (val >= (max_uint + 0.5)) ||
|
||||
(val < -0.5)
|
||||
: (val >= (max_int + 0.5)) ||
|
||||
(val < (min_int - 0.5));
|
||||
|
||||
case RM:
|
||||
return unsigned_ ? (val >= (max_uint + 1.0)) ||
|
||||
(val < 0)
|
||||
: (val >= (max_int + 1.0)) ||
|
||||
(val < min_int);
|
||||
|
||||
case RZ:
|
||||
return unsigned_ ? (val >= (max_uint + 1.0)) ||
|
||||
(val <= -1)
|
||||
: (val >= (max_int + 1.0)) ||
|
||||
(val <= (min_int - 1.0));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We call this function only if we had a vfp invalid exception.
|
||||
// It returns the correct saturated value.
|
||||
int VFPConversionSaturate(double val, bool unsigned_res) {
|
||||
if (val != val) {
|
||||
return 0;
|
||||
} else {
|
||||
if (unsigned_res) {
|
||||
return (val < 0) ? 0 : 0xffffffffu;
|
||||
} else {
|
||||
return (val < 0) ? kMinInt : kMaxInt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) {
|
||||
ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
|
||||
ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) &&
|
||||
(instr->Bits(27, 23) == 0x1D));
|
||||
ASSERT(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) ||
|
||||
(((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)));
|
||||
|
||||
// Conversion between floating-point and integer.
|
||||
bool to_integer = (instr->Bit(18) == 1);
|
||||
|
||||
VFPRegPrecision src_precision = kSinglePrecision;
|
||||
if (instr->SzValue() == 1) {
|
||||
src_precision = kDoublePrecision;
|
||||
}
|
||||
VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision
|
||||
: kSinglePrecision;
|
||||
|
||||
if (to_integer) {
|
||||
bool unsigned_integer = (instr->Bit(16) == 0);
|
||||
FPSCRRoundingModes mode;
|
||||
if (instr->Bit(7) != 1) {
|
||||
// Use FPSCR defined rounding mode.
|
||||
mode = FPSCR_rounding_mode_;
|
||||
// Only RZ and RM modes are supported.
|
||||
ASSERT((mode == RM) || (mode == RZ));
|
||||
} else {
|
||||
// VFP uses round towards zero by default.
|
||||
mode = RZ;
|
||||
}
|
||||
// We are playing with code close to the C++ standard's limits below,
|
||||
// hence the very simple code and heavy checks.
|
||||
//
|
||||
// Note:
|
||||
// C++ defines default type casting from floating point to integer as
|
||||
// (close to) rounding toward zero ("fractional part discarded").
|
||||
|
||||
int dst = instr->VFPDRegValue(kSinglePrecision);
|
||||
int src = instr->VFPMRegValue(src_precision);
|
||||
int32_t kMaxInt = v8::internal::kMaxInt;
|
||||
int32_t kMinInt = v8::internal::kMinInt;
|
||||
switch (mode) {
|
||||
case RM:
|
||||
if (src_precision == kDoublePrecision) {
|
||||
double val = get_double_from_d_register(src);
|
||||
|
||||
inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val);
|
||||
// Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding
|
||||
// mode or the default Round to Zero mode.
|
||||
VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_
|
||||
: RZ;
|
||||
ASSERT((mode == RM) || (mode == RZ) || (mode == RN));
|
||||
|
||||
int sint = unsigned_integer ? static_cast<uint32_t>(val) :
|
||||
static_cast<int32_t>(val);
|
||||
sint = sint > val ? sint - 1 : sint;
|
||||
bool unsigned_integer = (instr->Bit(16) == 0);
|
||||
bool double_precision = (src_precision == kDoublePrecision);
|
||||
|
||||
set_s_register_from_sinteger(dst, sint);
|
||||
double val = double_precision ? get_double_from_d_register(src)
|
||||
: get_float_from_s_register(src);
|
||||
|
||||
int temp = unsigned_integer ? static_cast<uint32_t>(val)
|
||||
: static_cast<int32_t>(val);
|
||||
|
||||
inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer);
|
||||
|
||||
if (inv_op_vfp_flag_) {
|
||||
temp = VFPConversionSaturate(val, unsigned_integer);
|
||||
} else {
|
||||
float val = get_float_from_s_register(src);
|
||||
|
||||
inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val);
|
||||
|
||||
int sint = unsigned_integer ? static_cast<uint32_t>(val) :
|
||||
static_cast<int32_t>(val);
|
||||
sint = sint > val ? sint - 1 : sint;
|
||||
|
||||
set_s_register_from_sinteger(dst, sint);
|
||||
switch (mode) {
|
||||
case RN: {
|
||||
double abs_diff =
|
||||
unsigned_integer ? fabs(val - static_cast<uint32_t>(temp))
|
||||
: fabs(val - temp);
|
||||
int val_sign = (val > 0) ? 1 : -1;
|
||||
if (abs_diff > 0.5) {
|
||||
temp += val_sign;
|
||||
} else if (abs_diff == 0.5) {
|
||||
// Round to even if exactly halfway.
|
||||
temp = ((temp % 2) == 0) ? temp : temp + val_sign;
|
||||
}
|
||||
break;
|
||||
case RZ:
|
||||
if (src_precision == kDoublePrecision) {
|
||||
double val = get_double_from_d_register(src);
|
||||
|
||||
inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val);
|
||||
|
||||
int sint = unsigned_integer ? static_cast<uint32_t>(val) :
|
||||
static_cast<int32_t>(val);
|
||||
|
||||
set_s_register_from_sinteger(dst, sint);
|
||||
} else {
|
||||
float val = get_float_from_s_register(src);
|
||||
|
||||
inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val);
|
||||
|
||||
int sint = unsigned_integer ? static_cast<uint32_t>(val) :
|
||||
static_cast<int32_t>(val);
|
||||
|
||||
set_s_register_from_sinteger(dst, sint);
|
||||
}
|
||||
|
||||
case RM:
|
||||
temp = temp > val ? temp - 1 : temp;
|
||||
break;
|
||||
|
||||
case RZ:
|
||||
// Nothing to do.
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the destination register.
|
||||
set_s_register_from_sinteger(dst, temp);
|
||||
|
||||
} else {
|
||||
bool unsigned_integer = (instr->Bit(7) == 0);
|
||||
|
@ -312,7 +312,7 @@ class Simulator {
|
||||
bool v_flag_FPSCR_;
|
||||
|
||||
// VFP rounding mode. See ARM DDI 0406B Page A2-29.
|
||||
FPSCRRoundingModes FPSCR_rounding_mode_;
|
||||
VFPRoundingMode FPSCR_rounding_mode_;
|
||||
|
||||
// VFP FP exception flags architecture state.
|
||||
bool inv_op_vfp_flag_;
|
||||
|
@ -2055,11 +2055,11 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
|
||||
// - Make sure Flush-to-zero mode control bit is unset (bit 22).
|
||||
__ bic(r9, r3,
|
||||
Operand(kVFPExceptionMask | kVFPRoundingModeMask | kVFPFlushToZeroMask));
|
||||
__ orr(r9, r9, Operand(kVFPRoundToMinusInfinityBits));
|
||||
__ orr(r9, r9, Operand(kRoundToMinusInf));
|
||||
__ vmsr(r9);
|
||||
|
||||
// Convert the argument to an integer.
|
||||
__ vcvt_s32_f64(s0, d1, Assembler::FPSCRRounding, al);
|
||||
__ vcvt_s32_f64(s0, d1, kFPSCRRounding);
|
||||
|
||||
// Use vcvt latency to start checking for special cases.
|
||||
// Get the argument exponent and clear the sign bit.
|
||||
@ -3796,9 +3796,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
|
||||
|
||||
// Not infinity or NaN simply convert to int.
|
||||
if (IsElementTypeSigned(array_type)) {
|
||||
__ vcvt_s32_f64(s0, d0, Assembler::RoundToZero, ne);
|
||||
__ vcvt_s32_f64(s0, d0, kDefaultRoundToZero, ne);
|
||||
} else {
|
||||
__ vcvt_u32_f64(s0, d0, Assembler::RoundToZero, ne);
|
||||
__ vcvt_u32_f64(s0, d0, kDefaultRoundToZero, ne);
|
||||
}
|
||||
__ vmov(r5, s0, ne);
|
||||
|
||||
|
@ -409,23 +409,61 @@ TEST(6) {
|
||||
}
|
||||
|
||||
|
||||
static void TestRoundingMode(int32_t mode, double value, int expected) {
|
||||
enum VCVTTypes {
|
||||
s32_f64,
|
||||
u32_f64
|
||||
};
|
||||
|
||||
static void TestRoundingMode(VCVTTypes types,
|
||||
VFPRoundingMode mode,
|
||||
double value,
|
||||
int expected,
|
||||
bool expected_exception = false) {
|
||||
InitializeVM();
|
||||
v8::HandleScope scope;
|
||||
|
||||
Assembler assm(NULL, 0);
|
||||
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
|
||||
Label wrong_exception;
|
||||
|
||||
__ vmrs(r1);
|
||||
// Set custom FPSCR.
|
||||
__ bic(r2, r1, Operand(((mode ^ 3) << 22) | 0xf));
|
||||
__ orr(r2, r2, Operand(mode << 22));
|
||||
__ bic(r2, r1, Operand(kVFPRoundingModeMask | kVFPExceptionMask));
|
||||
__ orr(r2, r2, Operand(mode));
|
||||
__ vmsr(r2);
|
||||
|
||||
// Load value, convert, and move back result to r0.
|
||||
// Load value, convert, and move back result to r0 if everything went well.
|
||||
__ vmov(d1, value);
|
||||
__ vcvt_s32_f64(s0, d1, Assembler::FPSCRRounding, al);
|
||||
__ vmov(r0, s0);
|
||||
switch (types) {
|
||||
case s32_f64:
|
||||
__ vcvt_s32_f64(s0, d1, kFPSCRRounding);
|
||||
break;
|
||||
|
||||
case u32_f64:
|
||||
__ vcvt_u32_f64(s0, d1, kFPSCRRounding);
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
// Check for vfp exceptions
|
||||
__ vmrs(r2);
|
||||
__ tst(r2, Operand(kVFPExceptionMask));
|
||||
// Check that we behaved as expected.
|
||||
__ b(&wrong_exception,
|
||||
expected_exception ? eq : ne);
|
||||
// There was no exception. Retrieve the result and return.
|
||||
__ vmov(r0, s0);
|
||||
__ mov(pc, Operand(lr));
|
||||
|
||||
// The exception behaviour is not what we expected.
|
||||
// Load a special value and return.
|
||||
__ bind(&wrong_exception);
|
||||
__ mov(r0, Operand(11223344));
|
||||
__ mov(pc, Operand(lr));
|
||||
|
||||
CodeDesc desc;
|
||||
@ -444,36 +482,116 @@ static void TestRoundingMode(int32_t mode, double value, int expected) {
|
||||
::printf("res = %d\n", res);
|
||||
CHECK_EQ(expected, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(7) {
|
||||
// Test vfp rounding modes.
|
||||
|
||||
// See ARM DDI 0406B Page A2-29.
|
||||
enum FPSCRRoungingMode {
|
||||
RN, // Round to Nearest.
|
||||
RP, // Round towards Plus Infinity.
|
||||
RM, // Round towards Minus Infinity.
|
||||
RZ // Round towards zero.
|
||||
};
|
||||
// s32_f64 (double to integer).
|
||||
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
TestRoundingMode(s32_f64, RN, 0, 0);
|
||||
TestRoundingMode(s32_f64, RN, 0.5, 0);
|
||||
TestRoundingMode(s32_f64, RN, -0.5, 0);
|
||||
TestRoundingMode(s32_f64, RN, 1.5, 2);
|
||||
TestRoundingMode(s32_f64, RN, -1.5, -2);
|
||||
TestRoundingMode(s32_f64, RN, 123.7, 124);
|
||||
TestRoundingMode(s32_f64, RN, -123.7, -124);
|
||||
TestRoundingMode(s32_f64, RN, 123456.2, 123456);
|
||||
TestRoundingMode(s32_f64, RN, -123456.2, -123456);
|
||||
TestRoundingMode(s32_f64, RN, static_cast<double>(kMaxInt), kMaxInt);
|
||||
TestRoundingMode(s32_f64, RN, (kMaxInt + 0.49), kMaxInt);
|
||||
TestRoundingMode(s32_f64, RN, (kMaxInt + 1.0), kMaxInt, true);
|
||||
TestRoundingMode(s32_f64, RN, (kMaxInt + 0.5), kMaxInt, true);
|
||||
TestRoundingMode(s32_f64, RN, static_cast<double>(kMinInt), kMinInt);
|
||||
TestRoundingMode(s32_f64, RN, (kMinInt - 0.5), kMinInt);
|
||||
TestRoundingMode(s32_f64, RN, (kMinInt - 1.0), kMinInt, true);
|
||||
TestRoundingMode(s32_f64, RN, (kMinInt - 0.51), kMinInt, true);
|
||||
|
||||
TestRoundingMode(RZ, 0.5, 0);
|
||||
TestRoundingMode(RZ, -0.5, 0);
|
||||
TestRoundingMode(RZ, 123.7, 123);
|
||||
TestRoundingMode(RZ, -123.7, -123);
|
||||
TestRoundingMode(RZ, 123456.2, 123456);
|
||||
TestRoundingMode(RZ, -123456.2, -123456);
|
||||
TestRoundingMode(s32_f64, RM, 0, 0);
|
||||
TestRoundingMode(s32_f64, RM, 0.5, 0);
|
||||
TestRoundingMode(s32_f64, RM, -0.5, -1);
|
||||
TestRoundingMode(s32_f64, RM, 123.7, 123);
|
||||
TestRoundingMode(s32_f64, RM, -123.7, -124);
|
||||
TestRoundingMode(s32_f64, RM, 123456.2, 123456);
|
||||
TestRoundingMode(s32_f64, RM, -123456.2, -123457);
|
||||
TestRoundingMode(s32_f64, RM, static_cast<double>(kMaxInt), kMaxInt);
|
||||
TestRoundingMode(s32_f64, RM, (kMaxInt + 0.5), kMaxInt);
|
||||
TestRoundingMode(s32_f64, RM, (kMaxInt + 1.0), kMaxInt, true);
|
||||
TestRoundingMode(s32_f64, RM, static_cast<double>(kMinInt), kMinInt);
|
||||
TestRoundingMode(s32_f64, RM, (kMinInt - 0.5), kMinInt, true);
|
||||
TestRoundingMode(s32_f64, RM, (kMinInt + 0.5), kMinInt);
|
||||
|
||||
TestRoundingMode(RM, 0.5, 0);
|
||||
TestRoundingMode(RM, -0.5, -1);
|
||||
TestRoundingMode(RM, 123.7, 123);
|
||||
TestRoundingMode(RM, -123.7, -124);
|
||||
TestRoundingMode(RM, 123456.2, 123456);
|
||||
TestRoundingMode(RM, -123456.2, -123457);
|
||||
}
|
||||
TestRoundingMode(s32_f64, RZ, 0, 0);
|
||||
TestRoundingMode(s32_f64, RZ, 0.5, 0);
|
||||
TestRoundingMode(s32_f64, RZ, -0.5, 0);
|
||||
TestRoundingMode(s32_f64, RZ, 123.7, 123);
|
||||
TestRoundingMode(s32_f64, RZ, -123.7, -123);
|
||||
TestRoundingMode(s32_f64, RZ, 123456.2, 123456);
|
||||
TestRoundingMode(s32_f64, RZ, -123456.2, -123456);
|
||||
TestRoundingMode(s32_f64, RZ, static_cast<double>(kMaxInt), kMaxInt);
|
||||
TestRoundingMode(s32_f64, RZ, (kMaxInt + 0.5), kMaxInt);
|
||||
TestRoundingMode(s32_f64, RZ, (kMaxInt + 1.0), kMaxInt, true);
|
||||
TestRoundingMode(s32_f64, RZ, static_cast<double>(kMinInt), kMinInt);
|
||||
TestRoundingMode(s32_f64, RZ, (kMinInt - 0.5), kMinInt);
|
||||
TestRoundingMode(s32_f64, RZ, (kMinInt - 1.0), kMinInt, true);
|
||||
|
||||
|
||||
// u32_f64 (double to integer).
|
||||
|
||||
// Negative values.
|
||||
TestRoundingMode(u32_f64, RN, -0.5, 0);
|
||||
TestRoundingMode(u32_f64, RN, -123456.7, 0, true);
|
||||
TestRoundingMode(u32_f64, RN, static_cast<double>(kMinInt), 0, true);
|
||||
TestRoundingMode(u32_f64, RN, kMinInt - 1.0, 0, true);
|
||||
|
||||
TestRoundingMode(u32_f64, RM, -0.5, 0, true);
|
||||
TestRoundingMode(u32_f64, RM, -123456.7, 0, true);
|
||||
TestRoundingMode(u32_f64, RM, static_cast<double>(kMinInt), 0, true);
|
||||
TestRoundingMode(u32_f64, RM, kMinInt - 1.0, 0, true);
|
||||
|
||||
TestRoundingMode(u32_f64, RZ, -0.5, 0);
|
||||
TestRoundingMode(u32_f64, RZ, -123456.7, 0, true);
|
||||
TestRoundingMode(u32_f64, RZ, static_cast<double>(kMinInt), 0, true);
|
||||
TestRoundingMode(u32_f64, RZ, kMinInt - 1.0, 0, true);
|
||||
|
||||
// Positive values.
|
||||
// kMaxInt is the maximum *signed* integer: 0x7fffffff.
|
||||
static const uint32_t kMaxUInt = 0xffffffffu;
|
||||
TestRoundingMode(u32_f64, RZ, 0, 0);
|
||||
TestRoundingMode(u32_f64, RZ, 0.5, 0);
|
||||
TestRoundingMode(u32_f64, RZ, 123.7, 123);
|
||||
TestRoundingMode(u32_f64, RZ, 123456.2, 123456);
|
||||
TestRoundingMode(u32_f64, RZ, static_cast<double>(kMaxInt), kMaxInt);
|
||||
TestRoundingMode(u32_f64, RZ, (kMaxInt + 0.5), kMaxInt);
|
||||
TestRoundingMode(u32_f64, RZ, (kMaxInt + 1.0),
|
||||
static_cast<uint32_t>(kMaxInt) + 1);
|
||||
TestRoundingMode(u32_f64, RZ, (kMaxUInt + 0.5), kMaxUInt);
|
||||
TestRoundingMode(u32_f64, RZ, (kMaxUInt + 1.0), kMaxUInt, true);
|
||||
|
||||
TestRoundingMode(u32_f64, RM, 0, 0);
|
||||
TestRoundingMode(u32_f64, RM, 0.5, 0);
|
||||
TestRoundingMode(u32_f64, RM, 123.7, 123);
|
||||
TestRoundingMode(u32_f64, RM, 123456.2, 123456);
|
||||
TestRoundingMode(u32_f64, RM, static_cast<double>(kMaxInt), kMaxInt);
|
||||
TestRoundingMode(u32_f64, RM, (kMaxInt + 0.5), kMaxInt);
|
||||
TestRoundingMode(u32_f64, RM, (kMaxInt + 1.0),
|
||||
static_cast<uint32_t>(kMaxInt) + 1);
|
||||
TestRoundingMode(u32_f64, RM, (kMaxUInt + 0.5), kMaxUInt);
|
||||
TestRoundingMode(u32_f64, RM, (kMaxUInt + 1.0), kMaxUInt, true);
|
||||
|
||||
TestRoundingMode(u32_f64, RN, 0, 0);
|
||||
TestRoundingMode(u32_f64, RN, 0.5, 0);
|
||||
TestRoundingMode(u32_f64, RN, 1.5, 2);
|
||||
TestRoundingMode(u32_f64, RN, 123.7, 124);
|
||||
TestRoundingMode(u32_f64, RN, 123456.2, 123456);
|
||||
TestRoundingMode(u32_f64, RN, static_cast<double>(kMaxInt), kMaxInt);
|
||||
TestRoundingMode(u32_f64, RN, (kMaxInt + 0.49), kMaxInt);
|
||||
TestRoundingMode(u32_f64, RN, (kMaxInt + 0.5),
|
||||
static_cast<uint32_t>(kMaxInt) + 1);
|
||||
TestRoundingMode(u32_f64, RN, (kMaxUInt + 0.49), kMaxUInt);
|
||||
TestRoundingMode(u32_f64, RN, (kMaxUInt + 0.5), kMaxUInt, true);
|
||||
TestRoundingMode(u32_f64, RN, (kMaxUInt + 1.0), kMaxUInt, true);
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
Loading…
Reference in New Issue
Block a user