Implement int32 TypeRecordingBinaryOp on ARM.
TEST=none BUG=none Patch by Rodolph Perfetta from ARM Ltd. Review URL: http://codereview.chromium.org/6594009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7014 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
617ccc1d93
commit
1703b8a35c
@ -284,6 +284,7 @@ const SwVfpRegister s29 = { 29 };
|
||||
const SwVfpRegister s30 = { 30 };
|
||||
const SwVfpRegister s31 = { 31 };
|
||||
|
||||
const DwVfpRegister no_dreg = { -1 };
|
||||
const DwVfpRegister d0 = { 0 };
|
||||
const DwVfpRegister d1 = { 1 };
|
||||
const DwVfpRegister d2 = { 2 };
|
||||
|
@ -398,8 +398,11 @@ class FloatingPointHelper : public AllStatic {
|
||||
Label* not_number);
|
||||
|
||||
// Loads the number from object into dst as a 32-bit integer if possible. If
|
||||
// the object is not a 32-bit integer control continues at the label
|
||||
// not_int32. If VFP is supported double_scratch is used but not scratch2.
|
||||
// the object cannot be converted to a 32-bit integer control continues at
|
||||
// the label not_int32. If VFP is supported double_scratch is used
|
||||
// but not scratch2.
|
||||
// Floating point value in the 32-bit integer range will be rounded
|
||||
// to an integer.
|
||||
static void LoadNumberAsInteger(MacroAssembler* masm,
|
||||
Register object,
|
||||
Register dst,
|
||||
@ -409,6 +412,76 @@ class FloatingPointHelper : public AllStatic {
|
||||
DwVfpRegister double_scratch,
|
||||
Label* not_int32);
|
||||
|
||||
// Load the number from object into double_dst in the double format.
|
||||
// Control will jump to not_int32 if the value cannot be exactly represented
|
||||
// by a 32-bit integer.
|
||||
// Floating point value in the 32-bit integer range that are not exact integer
|
||||
// won't be loaded.
|
||||
static void LoadNumberAsInt32Double(MacroAssembler* masm,
|
||||
Register object,
|
||||
Destination destination,
|
||||
DwVfpRegister double_dst,
|
||||
Register dst1,
|
||||
Register dst2,
|
||||
Register heap_number_map,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
SwVfpRegister single_scratch,
|
||||
Label* not_int32);
|
||||
|
||||
// Loads the number from object into dst as a 32-bit integer.
|
||||
// Control will jump to not_int32 if the object cannot be exactly represented
|
||||
// by a 32-bit integer.
|
||||
// Floating point value in the 32-bit integer range that are not exact integer
|
||||
// won't be converted.
|
||||
// scratch3 is not used when VFP3 is supported.
|
||||
static void LoadNumberAsInt32(MacroAssembler* masm,
|
||||
Register object,
|
||||
Register dst,
|
||||
Register heap_number_map,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
DwVfpRegister double_scratch,
|
||||
Label* not_int32);
|
||||
|
||||
// Generate non VFP3 code to check if a double can be exactly represented by a
|
||||
// 32-bit integer. This does not check for 0 or -0, which need
|
||||
// to be checked for separately.
|
||||
// Control jumps to not_int32 if the value is not a 32-bit integer, and falls
|
||||
// through otherwise.
|
||||
// src1 and src2 will be cloberred.
|
||||
//
|
||||
// Expected input:
|
||||
// - src1: higher (exponent) part of the double value.
|
||||
// - src2: lower (mantissa) part of the double value.
|
||||
// Output status:
|
||||
// - dst: 32 higher bits of the mantissa. (mantissa[51:20])
|
||||
// - src2: contains 1.
|
||||
// - other registers are clobbered.
|
||||
static void DoubleIs32BitInteger(MacroAssembler* masm,
|
||||
Register src1,
|
||||
Register src2,
|
||||
Register dst,
|
||||
Register scratch,
|
||||
Label* not_int32);
|
||||
|
||||
// Generates code to call a C function to do a double operation using core
|
||||
// registers. (Used when VFP3 is not supported.)
|
||||
// This code never falls through, but returns with a heap number containing
|
||||
// the result in r0.
|
||||
// Register heapnumber_result must be a heap number in which the
|
||||
// result of the operation will be stored.
|
||||
// Requires the following layout on entry:
|
||||
// r0: Left value (least significant part of mantissa).
|
||||
// r1: Left value (sign, exponent, top of mantissa).
|
||||
// r2: Right value (least significant part of mantissa).
|
||||
// r3: Right value (sign, exponent, top of mantissa).
|
||||
static void CallCCodeForDoubleOperation(MacroAssembler* masm,
|
||||
Token::Value op,
|
||||
Register heap_number_result,
|
||||
Register scratch);
|
||||
|
||||
private:
|
||||
static void LoadNumber(MacroAssembler* masm,
|
||||
FloatingPointHelper::Destination destination,
|
||||
@ -560,6 +633,319 @@ void FloatingPointHelper::LoadNumberAsInteger(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
|
||||
Register object,
|
||||
Destination destination,
|
||||
DwVfpRegister double_dst,
|
||||
Register dst1,
|
||||
Register dst2,
|
||||
Register heap_number_map,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
SwVfpRegister single_scratch,
|
||||
Label* not_int32) {
|
||||
ASSERT(!scratch1.is(object) && !scratch2.is(object));
|
||||
ASSERT(!scratch1.is(scratch2));
|
||||
ASSERT(!heap_number_map.is(object) &&
|
||||
!heap_number_map.is(scratch1) &&
|
||||
!heap_number_map.is(scratch2));
|
||||
|
||||
Label done, obj_is_not_smi;
|
||||
|
||||
__ JumpIfNotSmi(object, &obj_is_not_smi);
|
||||
__ SmiUntag(scratch1, object);
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
__ vmov(single_scratch, scratch1);
|
||||
__ vcvt_f64_s32(double_dst, single_scratch);
|
||||
if (destination == kCoreRegisters) {
|
||||
__ vmov(dst1, dst2, double_dst);
|
||||
}
|
||||
} else {
|
||||
Label fewer_than_20_useful_bits;
|
||||
// Expected output:
|
||||
// | dst1 | dst2 |
|
||||
// | s | exp | mantissa |
|
||||
|
||||
// Check for zero.
|
||||
__ cmp(scratch1, Operand(0));
|
||||
__ mov(dst1, scratch1);
|
||||
__ mov(dst2, scratch1);
|
||||
__ b(eq, &done);
|
||||
|
||||
// Preload the sign of the value.
|
||||
__ and_(dst1, scratch1, Operand(HeapNumber::kSignMask), SetCC);
|
||||
// Get the absolute value of the object (as an unsigned integer).
|
||||
__ rsb(scratch1, scratch1, Operand(0), SetCC, mi);
|
||||
|
||||
// Get mantisssa[51:20].
|
||||
|
||||
// Get the position of the first set bit.
|
||||
__ CountLeadingZeros(dst2, scratch1, scratch2);
|
||||
__ rsb(dst2, dst2, Operand(31));
|
||||
|
||||
// Set the exponent.
|
||||
__ add(scratch2, dst2, Operand(HeapNumber::kExponentBias));
|
||||
__ Bfi(dst1, scratch2, scratch2,
|
||||
HeapNumber::kExponentShift, HeapNumber::kExponentBits);
|
||||
|
||||
// Clear the first non null bit.
|
||||
__ mov(scratch2, Operand(1));
|
||||
__ bic(scratch1, scratch1, Operand(scratch2, LSL, dst2));
|
||||
|
||||
__ cmp(dst2, Operand(HeapNumber::kMantissaBitsInTopWord));
|
||||
// Get the number of bits to set in the lower part of the mantissa.
|
||||
__ sub(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC);
|
||||
__ b(mi, &fewer_than_20_useful_bits);
|
||||
// Set the higher 20 bits of the mantissa.
|
||||
__ orr(dst1, dst1, Operand(scratch1, LSR, scratch2));
|
||||
__ rsb(scratch2, scratch2, Operand(32));
|
||||
__ mov(dst2, Operand(scratch1, LSL, scratch2));
|
||||
__ b(&done);
|
||||
|
||||
__ bind(&fewer_than_20_useful_bits);
|
||||
__ rsb(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord));
|
||||
__ mov(scratch2, Operand(scratch1, LSL, scratch2));
|
||||
__ orr(dst1, dst1, scratch2);
|
||||
// Set dst2 to 0.
|
||||
__ mov(dst2, Operand(0));
|
||||
}
|
||||
|
||||
__ b(&done);
|
||||
|
||||
__ bind(&obj_is_not_smi);
|
||||
if (FLAG_debug_code) {
|
||||
__ AbortIfNotRootValue(heap_number_map,
|
||||
Heap::kHeapNumberMapRootIndex,
|
||||
"HeapNumberMap register clobbered.");
|
||||
}
|
||||
__ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
|
||||
|
||||
// Load the number.
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
// Load the double value.
|
||||
__ sub(scratch1, object, Operand(kHeapObjectTag));
|
||||
__ vldr(double_dst, scratch1, HeapNumber::kValueOffset);
|
||||
|
||||
__ EmitVFPTruncate(kRoundToZero,
|
||||
single_scratch,
|
||||
double_dst,
|
||||
scratch1,
|
||||
scratch2,
|
||||
kCheckForInexactConversion);
|
||||
|
||||
// Jump to not_int32 if the operation did not succeed.
|
||||
__ b(ne, not_int32);
|
||||
|
||||
if (destination == kCoreRegisters) {
|
||||
__ vmov(dst1, dst2, double_dst);
|
||||
}
|
||||
|
||||
} else {
|
||||
ASSERT(!scratch1.is(object) && !scratch2.is(object));
|
||||
// Load the double value in the destination registers..
|
||||
__ Ldrd(dst1, dst2, FieldMemOperand(object, HeapNumber::kValueOffset));
|
||||
|
||||
// Check for 0 and -0.
|
||||
__ bic(scratch1, dst1, Operand(HeapNumber::kSignMask));
|
||||
__ orr(scratch1, scratch1, Operand(dst2));
|
||||
__ cmp(scratch1, Operand(0));
|
||||
__ b(eq, &done);
|
||||
|
||||
// Check that the value can be exactly represented by a 32-bit integer.
|
||||
// Jump to not_int32 if that's not the case.
|
||||
DoubleIs32BitInteger(masm, dst1, dst2, scratch1, scratch2, not_int32);
|
||||
|
||||
// dst1 and dst2 were trashed. Reload the double value.
|
||||
__ Ldrd(dst1, dst2, FieldMemOperand(object, HeapNumber::kValueOffset));
|
||||
}
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm,
|
||||
Register object,
|
||||
Register dst,
|
||||
Register heap_number_map,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
DwVfpRegister double_scratch,
|
||||
Label* not_int32) {
|
||||
ASSERT(!dst.is(object));
|
||||
ASSERT(!scratch1.is(object) && !scratch2.is(object) && !scratch3.is(object));
|
||||
ASSERT(!scratch1.is(scratch2) &&
|
||||
!scratch1.is(scratch3) &&
|
||||
!scratch2.is(scratch3));
|
||||
|
||||
Label done;
|
||||
|
||||
// Untag the object into the destination register.
|
||||
__ SmiUntag(dst, object);
|
||||
// Just return if the object is a smi.
|
||||
__ JumpIfSmi(object, &done);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ AbortIfNotRootValue(heap_number_map,
|
||||
Heap::kHeapNumberMapRootIndex,
|
||||
"HeapNumberMap register clobbered.");
|
||||
}
|
||||
__ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
|
||||
|
||||
// Object is a heap number.
|
||||
// Convert the floating point value to a 32-bit integer.
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
SwVfpRegister single_scratch = double_scratch.low();
|
||||
// Load the double value.
|
||||
__ sub(scratch1, object, Operand(kHeapObjectTag));
|
||||
__ vldr(double_scratch, scratch1, HeapNumber::kValueOffset);
|
||||
|
||||
__ EmitVFPTruncate(kRoundToZero,
|
||||
single_scratch,
|
||||
double_scratch,
|
||||
scratch1,
|
||||
scratch2,
|
||||
kCheckForInexactConversion);
|
||||
|
||||
// Jump to not_int32 if the operation did not succeed.
|
||||
__ b(ne, not_int32);
|
||||
// Get the result in the destination register.
|
||||
__ vmov(dst, single_scratch);
|
||||
|
||||
} else {
|
||||
// Load the double value in the destination registers.
|
||||
__ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
|
||||
__ ldr(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
|
||||
|
||||
// Check for 0 and -0.
|
||||
__ bic(dst, scratch1, Operand(HeapNumber::kSignMask));
|
||||
__ orr(dst, scratch2, Operand(dst));
|
||||
__ cmp(dst, Operand(0));
|
||||
__ b(eq, &done);
|
||||
|
||||
DoubleIs32BitInteger(masm, scratch1, scratch2, dst, scratch3, not_int32);
|
||||
|
||||
// Registers state after DoubleIs32BitInteger.
|
||||
// dst: mantissa[51:20].
|
||||
// scratch2: 1
|
||||
|
||||
// Shift back the higher bits of the mantissa.
|
||||
__ mov(dst, Operand(dst, LSR, scratch3));
|
||||
// Set the implicit first bit.
|
||||
__ rsb(scratch3, scratch3, Operand(32));
|
||||
__ orr(dst, dst, Operand(scratch2, LSL, scratch3));
|
||||
// Set the sign.
|
||||
__ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
|
||||
__ tst(scratch1, Operand(HeapNumber::kSignMask));
|
||||
__ rsb(dst, dst, Operand(0), LeaveCC, mi);
|
||||
}
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm,
|
||||
Register src1,
|
||||
Register src2,
|
||||
Register dst,
|
||||
Register scratch,
|
||||
Label* not_int32) {
|
||||
// Get exponent alone in scratch.
|
||||
__ Ubfx(scratch,
|
||||
src1,
|
||||
HeapNumber::kExponentShift,
|
||||
HeapNumber::kExponentBits);
|
||||
|
||||
// Substract the bias from the exponent.
|
||||
__ sub(scratch, scratch, Operand(HeapNumber::kExponentBias), SetCC);
|
||||
|
||||
// src1: higher (exponent) part of the double value.
|
||||
// src2: lower (mantissa) part of the double value.
|
||||
// scratch: unbiased exponent.
|
||||
|
||||
// Fast cases. Check for obvious non 32-bit integer values.
|
||||
// Negative exponent cannot yield 32-bit integers.
|
||||
__ b(mi, not_int32);
|
||||
// Exponent greater than 31 cannot yield 32-bit integers.
|
||||
// Also, a positive value with an exponent equal to 31 is outside of the
|
||||
// signed 32-bit integer range.
|
||||
__ tst(src1, Operand(HeapNumber::kSignMask));
|
||||
__ cmp(scratch, Operand(30), eq); // Executed for positive. If exponent is 30
|
||||
// the gt condition will be "correct" and
|
||||
// the next instruction will be skipped.
|
||||
__ cmp(scratch, Operand(31), ne); // Executed for negative and positive where
|
||||
// exponent is not 30.
|
||||
__ b(gt, not_int32);
|
||||
// - Bits [21:0] in the mantissa are not null.
|
||||
__ tst(src2, Operand(0x3fffff));
|
||||
__ b(ne, not_int32);
|
||||
|
||||
// Otherwise the exponent needs to be big enough to shift left all the
|
||||
// non zero bits left. So we need the (30 - exponent) last bits of the
|
||||
// 31 higher bits of the mantissa to be null.
|
||||
// Because bits [21:0] are null, we can check instead that the
|
||||
// (32 - exponent) last bits of the 32 higher bits of the mantisssa are null.
|
||||
|
||||
// Get the 32 higher bits of the mantissa in dst.
|
||||
__ Ubfx(dst,
|
||||
src2,
|
||||
HeapNumber::kMantissaBitsInTopWord,
|
||||
32 - HeapNumber::kMantissaBitsInTopWord);
|
||||
__ orr(dst,
|
||||
dst,
|
||||
Operand(src1, LSL, HeapNumber::kNonMantissaBitsInTopWord));
|
||||
|
||||
// Create the mask and test the lower bits (of the higher bits).
|
||||
__ rsb(scratch, scratch, Operand(32));
|
||||
__ mov(src2, Operand(1));
|
||||
__ mov(src1, Operand(src2, LSL, scratch));
|
||||
__ sub(src1, src1, Operand(1));
|
||||
__ tst(dst, src1);
|
||||
__ b(ne, not_int32);
|
||||
}
|
||||
|
||||
|
||||
void FloatingPointHelper::CallCCodeForDoubleOperation(
|
||||
MacroAssembler* masm,
|
||||
Token::Value op,
|
||||
Register heap_number_result,
|
||||
Register scratch) {
|
||||
// Using core registers:
|
||||
// r0: Left value (least significant part of mantissa).
|
||||
// r1: Left value (sign, exponent, top of mantissa).
|
||||
// r2: Right value (least significant part of mantissa).
|
||||
// r3: Right value (sign, exponent, top of mantissa).
|
||||
|
||||
// Assert that heap_number_result is callee-saved.
|
||||
// We currently always use r5 to pass it.
|
||||
ASSERT(heap_number_result.is(r5));
|
||||
|
||||
// Push the current return address before the C call. Return will be
|
||||
// through pop(pc) below.
|
||||
__ push(lr);
|
||||
__ PrepareCallCFunction(4, scratch); // Two doubles are 4 arguments.
|
||||
// Call C routine that may not cause GC or other trouble.
|
||||
__ CallCFunction(ExternalReference::double_fp_operation(op), 4);
|
||||
// Store answer in the overwritable heap number.
|
||||
#if !defined(USE_ARM_EABI)
|
||||
// Double returned in fp coprocessor register 0 and 1, encoded as
|
||||
// register cr8. Offsets must be divisible by 4 for coprocessor so we
|
||||
// need to substract the tag from heap_number_result.
|
||||
__ sub(scratch, heap_number_result, Operand(kHeapObjectTag));
|
||||
__ stc(p1, cr8, MemOperand(scratch, HeapNumber::kValueOffset));
|
||||
#else
|
||||
// Double returned in registers 0 and 1.
|
||||
__ Strd(r0, r1, FieldMemOperand(heap_number_result,
|
||||
HeapNumber::kValueOffset));
|
||||
#endif
|
||||
// Place heap_number_result in r0 and return to the pushed return address.
|
||||
__ mov(r0, Operand(heap_number_result));
|
||||
__ pop(pc);
|
||||
}
|
||||
|
||||
|
||||
// See comment for class.
|
||||
void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
|
||||
@ -2707,33 +3093,11 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
|
||||
__ add(r0, r0, Operand(kHeapObjectTag));
|
||||
__ Ret();
|
||||
} else {
|
||||
// Using core registers:
|
||||
// r0: Left value (least significant part of mantissa).
|
||||
// r1: Left value (sign, exponent, top of mantissa).
|
||||
// r2: Right value (least significant part of mantissa).
|
||||
// r3: Right value (sign, exponent, top of mantissa).
|
||||
|
||||
// Push the current return address before the C call. Return will be
|
||||
// through pop(pc) below.
|
||||
__ push(lr);
|
||||
__ PrepareCallCFunction(4, scratch1); // Two doubles are 4 arguments.
|
||||
// Call C routine that may not cause GC or other trouble. r5 is callee
|
||||
// save.
|
||||
__ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
|
||||
// Store answer in the overwritable heap number.
|
||||
#if !defined(USE_ARM_EABI)
|
||||
// Double returned in fp coprocessor register 0 and 1, encoded as
|
||||
// register cr8. Offsets must be divisible by 4 for coprocessor so we
|
||||
// need to substract the tag from r5.
|
||||
__ sub(scratch1, result, Operand(kHeapObjectTag));
|
||||
__ stc(p1, cr8, MemOperand(scratch1, HeapNumber::kValueOffset));
|
||||
#else
|
||||
// Double returned in registers 0 and 1.
|
||||
__ Strd(r0, r1, FieldMemOperand(result, HeapNumber::kValueOffset));
|
||||
#endif
|
||||
// Plase result in r0 and return to the pushed return address.
|
||||
__ mov(r0, Operand(result));
|
||||
__ pop(pc);
|
||||
// Call the C function to handle the double operation.
|
||||
FloatingPointHelper::CallCCodeForDoubleOperation(masm,
|
||||
op_,
|
||||
result,
|
||||
scratch1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2779,7 +3143,6 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
|
||||
break;
|
||||
case Token::SAR:
|
||||
// Use only the 5 least significant bits of the shift count.
|
||||
__ and_(r2, r2, Operand(0x1f));
|
||||
__ GetLeastBitsFromInt32(r2, r2, 5);
|
||||
__ mov(r2, Operand(r3, ASR, r2));
|
||||
break;
|
||||
@ -2924,7 +3287,288 @@ void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
|
||||
void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
|
||||
ASSERT(operands_type_ == TRBinaryOpIC::INT32);
|
||||
|
||||
GenerateTypeTransition(masm);
|
||||
Register left = r1;
|
||||
Register right = r0;
|
||||
Register scratch1 = r7;
|
||||
Register scratch2 = r9;
|
||||
DwVfpRegister double_scratch = d0;
|
||||
SwVfpRegister single_scratch = s3;
|
||||
|
||||
Register heap_number_result = no_reg;
|
||||
Register heap_number_map = r6;
|
||||
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||
|
||||
Label call_runtime;
|
||||
// Labels for type transition, used for wrong input or output types.
|
||||
// Both label are currently actually bound to the same position. We use two
|
||||
// different label to differentiate the cause leading to type transition.
|
||||
Label transition;
|
||||
|
||||
// Smi-smi fast case.
|
||||
Label skip;
|
||||
__ orr(scratch1, left, right);
|
||||
__ JumpIfNotSmi(scratch1, &skip);
|
||||
GenerateSmiSmiOperation(masm);
|
||||
// Fall through if the result is not a smi.
|
||||
__ bind(&skip);
|
||||
|
||||
switch (op_) {
|
||||
case Token::ADD:
|
||||
case Token::SUB:
|
||||
case Token::MUL:
|
||||
case Token::DIV:
|
||||
case Token::MOD: {
|
||||
// Load both operands and check that they are 32-bit integer.
|
||||
// Jump to type transition if they are not. The registers r0 and r1 (right
|
||||
// and left) are preserved for the runtime call.
|
||||
FloatingPointHelper::Destination destination =
|
||||
CpuFeatures::IsSupported(VFP3) && op_ != Token::MOD ?
|
||||
FloatingPointHelper::kVFPRegisters :
|
||||
FloatingPointHelper::kCoreRegisters;
|
||||
|
||||
FloatingPointHelper::LoadNumberAsInt32Double(masm,
|
||||
right,
|
||||
destination,
|
||||
d7,
|
||||
r2,
|
||||
r3,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
s0,
|
||||
&transition);
|
||||
FloatingPointHelper::LoadNumberAsInt32Double(masm,
|
||||
left,
|
||||
destination,
|
||||
d6,
|
||||
r4,
|
||||
r5,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
s0,
|
||||
&transition);
|
||||
|
||||
if (destination == FloatingPointHelper::kVFPRegisters) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
Label return_heap_number;
|
||||
switch (op_) {
|
||||
case Token::ADD:
|
||||
__ vadd(d5, d6, d7);
|
||||
break;
|
||||
case Token::SUB:
|
||||
__ vsub(d5, d6, d7);
|
||||
break;
|
||||
case Token::MUL:
|
||||
__ vmul(d5, d6, d7);
|
||||
break;
|
||||
case Token::DIV:
|
||||
__ vdiv(d5, d6, d7);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (op_ != Token::DIV) {
|
||||
// These operations produce an integer result.
|
||||
// Try to return a smi if we can.
|
||||
// Otherwise return a heap number if allowed, or jump to type
|
||||
// transition.
|
||||
|
||||
__ EmitVFPTruncate(kRoundToZero,
|
||||
single_scratch,
|
||||
d5,
|
||||
scratch1,
|
||||
scratch2);
|
||||
|
||||
if (result_type_ <= TRBinaryOpIC::INT32) {
|
||||
// If the ne condition is set, result does
|
||||
// not fit in a 32-bit integer.
|
||||
__ b(ne, &transition);
|
||||
}
|
||||
|
||||
// Check if the result fits in a smi.
|
||||
__ vmov(scratch1, single_scratch);
|
||||
__ add(scratch2, scratch1, Operand(0x40000000), SetCC);
|
||||
// If not try to return a heap number.
|
||||
__ b(mi, &return_heap_number);
|
||||
// Tag the result and return.
|
||||
__ SmiTag(r0, scratch1);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
if (result_type_ >= (op_ == Token::DIV) ? TRBinaryOpIC::HEAP_NUMBER
|
||||
: TRBinaryOpIC::INT32) {
|
||||
__ bind(&return_heap_number);
|
||||
// We are using vfp registers so r5 is available.
|
||||
heap_number_result = r5;
|
||||
GenerateHeapResultAllocation(masm,
|
||||
heap_number_result,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
&call_runtime);
|
||||
__ sub(r0, heap_number_result, Operand(kHeapObjectTag));
|
||||
__ vstr(d5, r0, HeapNumber::kValueOffset);
|
||||
__ mov(r0, heap_number_result);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
// A DIV operation expecting an integer result falls through
|
||||
// to type transition.
|
||||
|
||||
} else {
|
||||
// We preserved r0 and r1 to be able to call runtime.
|
||||
// Save the left value on the stack.
|
||||
__ Push(r5, r4);
|
||||
|
||||
// Allocate a heap number to store the result.
|
||||
heap_number_result = r5;
|
||||
GenerateHeapResultAllocation(masm,
|
||||
heap_number_result,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
&call_runtime);
|
||||
|
||||
// Load the left value from the value saved on the stack.
|
||||
__ Pop(r1, r0);
|
||||
|
||||
// Call the C function to handle the double operation.
|
||||
FloatingPointHelper::CallCCodeForDoubleOperation(
|
||||
masm, op_, heap_number_result, scratch1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Token::BIT_OR:
|
||||
case Token::BIT_XOR:
|
||||
case Token::BIT_AND:
|
||||
case Token::SAR:
|
||||
case Token::SHR:
|
||||
case Token::SHL: {
|
||||
Label return_heap_number;
|
||||
Register scratch3 = r5;
|
||||
// Convert operands to 32-bit integers. Right in r2 and left in r3. The
|
||||
// registers r0 and r1 (right and left) are preserved for the runtime
|
||||
// call.
|
||||
FloatingPointHelper::LoadNumberAsInt32(masm,
|
||||
left,
|
||||
r3,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
scratch3,
|
||||
d0,
|
||||
&transition);
|
||||
FloatingPointHelper::LoadNumberAsInt32(masm,
|
||||
right,
|
||||
r2,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
scratch3,
|
||||
d0,
|
||||
&transition);
|
||||
|
||||
// The ECMA-262 standard specifies that, for shift operations, only the
|
||||
// 5 least significant bits of the shift value should be used.
|
||||
switch (op_) {
|
||||
case Token::BIT_OR:
|
||||
__ orr(r2, r3, Operand(r2));
|
||||
break;
|
||||
case Token::BIT_XOR:
|
||||
__ eor(r2, r3, Operand(r2));
|
||||
break;
|
||||
case Token::BIT_AND:
|
||||
__ and_(r2, r3, Operand(r2));
|
||||
break;
|
||||
case Token::SAR:
|
||||
__ and_(r2, r2, Operand(0x1f));
|
||||
__ mov(r2, Operand(r3, ASR, r2));
|
||||
break;
|
||||
case Token::SHR:
|
||||
__ and_(r2, r2, Operand(0x1f));
|
||||
__ mov(r2, Operand(r3, LSR, r2), SetCC);
|
||||
// SHR is special because it is required to produce a positive answer.
|
||||
// We only get a negative result if the shift value (r2) is 0.
|
||||
// This result cannot be respresented as a signed 32-bit integer, try
|
||||
// to return a heap number if we can.
|
||||
// The non vfp3 code does not support this special case, so jump to
|
||||
// runtime if we don't support it.
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
__ b(mi,
|
||||
(result_type_ <= TRBinaryOpIC::INT32) ? &transition
|
||||
: &return_heap_number);
|
||||
} else {
|
||||
__ b(mi, (result_type_ <= TRBinaryOpIC::INT32) ? &transition
|
||||
: &call_runtime);
|
||||
}
|
||||
break;
|
||||
case Token::SHL:
|
||||
__ and_(r2, r2, Operand(0x1f));
|
||||
__ mov(r2, Operand(r3, LSL, r2));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Check if the result fits in a smi.
|
||||
__ add(scratch1, r2, Operand(0x40000000), SetCC);
|
||||
// If not try to return a heap number. (We know the result is an int32.)
|
||||
__ b(mi, &return_heap_number);
|
||||
// Tag the result and return.
|
||||
__ SmiTag(r0, r2);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&return_heap_number);
|
||||
if (CpuFeatures::IsSupported(VFP3)) {
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
heap_number_result = r5;
|
||||
GenerateHeapResultAllocation(masm,
|
||||
heap_number_result,
|
||||
heap_number_map,
|
||||
scratch1,
|
||||
scratch2,
|
||||
&call_runtime);
|
||||
|
||||
if (op_ != Token::SHR) {
|
||||
// Convert the result to a floating point value.
|
||||
__ vmov(double_scratch.low(), r2);
|
||||
__ vcvt_f64_s32(double_scratch, double_scratch.low());
|
||||
} else {
|
||||
// The result must be interpreted as an unsigned 32-bit integer.
|
||||
__ vmov(double_scratch.low(), r2);
|
||||
__ vcvt_f64_u32(double_scratch, double_scratch.low());
|
||||
}
|
||||
|
||||
// Store the result.
|
||||
__ sub(r0, heap_number_result, Operand(kHeapObjectTag));
|
||||
__ vstr(double_scratch, r0, HeapNumber::kValueOffset);
|
||||
__ mov(r0, heap_number_result);
|
||||
__ Ret();
|
||||
} else {
|
||||
// Tail call that writes the int32 in r2 to the heap number in r0, using
|
||||
// r3 as scratch. r0 is preserved and returned.
|
||||
WriteInt32ToHeapNumberStub stub(r2, r0, r3);
|
||||
__ TailCallStub(&stub);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (transition.is_linked()) {
|
||||
__ bind(&transition);
|
||||
GenerateTypeTransition(masm);
|
||||
}
|
||||
|
||||
__ bind(&call_runtime);
|
||||
GenerateCallRuntime(masm);
|
||||
}
|
||||
|
||||
|
||||
|
@ -385,7 +385,10 @@ enum VFPConversionMode {
|
||||
kDefaultRoundToZero = 1
|
||||
};
|
||||
|
||||
// This mask does not include the "inexact" or "input denormal" cumulative
|
||||
// exceptions flags, because we usually don't want to check for it.
|
||||
static const uint32_t kVFPExceptionMask = 0xf;
|
||||
static const uint32_t kVFPInexactExceptionBit = 1 << 4;
|
||||
static const uint32_t kVFPFlushToZeroMask = 1 << 24;
|
||||
static const uint32_t kVFPInvalidExceptionBit = 1;
|
||||
|
||||
@ -411,6 +414,11 @@ enum VFPRoundingMode {
|
||||
|
||||
static const uint32_t kVFPRoundingModeMask = 3 << 22;
|
||||
|
||||
enum CheckForInexactConversion {
|
||||
kCheckForInexactConversion,
|
||||
kDontCheckForInexactConversion
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Hints.
|
||||
|
||||
|
@ -2583,41 +2583,6 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
// - 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(rounding_mode));
|
||||
__ vmsr(scratch);
|
||||
|
||||
// Convert the argument to an integer.
|
||||
__ vcvt_s32_f64(result,
|
||||
double_input,
|
||||
kFPSCRRounding);
|
||||
|
||||
// Retrieve FPSCR.
|
||||
__ vmrs(scratch);
|
||||
// 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());
|
||||
@ -2625,11 +2590,11 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
|
||||
Register scratch1 = scratch0();
|
||||
Register scratch2 = ToRegister(instr->TempAt(0));
|
||||
|
||||
EmitVFPTruncate(kRoundToMinusInf,
|
||||
single_scratch,
|
||||
input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
__ EmitVFPTruncate(kRoundToMinusInf,
|
||||
single_scratch,
|
||||
input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
|
||||
// Move the result back to general purpose register r0.
|
||||
@ -2651,11 +2616,11 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Register scratch1 = scratch0();
|
||||
Register scratch2 = result;
|
||||
EmitVFPTruncate(kRoundToNearest,
|
||||
double_scratch0().low(),
|
||||
input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
__ EmitVFPTruncate(kRoundToNearest,
|
||||
double_scratch0().low(),
|
||||
input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
__ vmov(result, double_scratch0().low());
|
||||
|
||||
@ -3370,11 +3335,12 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
|
||||
Register scratch1 = scratch0();
|
||||
Register scratch2 = ToRegister(instr->TempAt(0));
|
||||
|
||||
EmitVFPTruncate(kRoundToZero,
|
||||
single_scratch,
|
||||
double_input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
__ EmitVFPTruncate(kRoundToZero,
|
||||
single_scratch,
|
||||
double_input,
|
||||
scratch1,
|
||||
scratch2);
|
||||
|
||||
// Deoptimize if we had a vfp invalid exception.
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
|
||||
|
@ -206,11 +206,6 @@ 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 DoMathRound(LUnaryMathOperation* instr);
|
||||
void DoMathSqrt(LUnaryMathOperation* instr);
|
||||
|
@ -271,6 +271,29 @@ void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Bfi(Register dst,
|
||||
Register src,
|
||||
Register scratch,
|
||||
int lsb,
|
||||
int width,
|
||||
Condition cond) {
|
||||
ASSERT(0 <= lsb && lsb < 32);
|
||||
ASSERT(0 <= width && width < 32);
|
||||
ASSERT(lsb + width < 32);
|
||||
ASSERT(!scratch.is(dst));
|
||||
if (width == 0) return;
|
||||
if (!CpuFeatures::IsSupported(ARMv7)) {
|
||||
int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
|
||||
bic(dst, dst, Operand(mask));
|
||||
and_(scratch, src, Operand((1 << width) - 1));
|
||||
mov(scratch, Operand(scratch, LSL, lsb));
|
||||
orr(dst, dst, scratch);
|
||||
} else {
|
||||
bfi(dst, src, lsb, width, cond);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Bfc(Register dst, int lsb, int width, Condition cond) {
|
||||
ASSERT(lsb < 32);
|
||||
if (!CpuFeatures::IsSupported(ARMv7)) {
|
||||
@ -1818,9 +1841,9 @@ void MacroAssembler::ConvertToInt32(Register source,
|
||||
ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
|
||||
// Get exponent alone in scratch2.
|
||||
Ubfx(scratch2,
|
||||
scratch,
|
||||
HeapNumber::kExponentShift,
|
||||
HeapNumber::kExponentBits);
|
||||
scratch,
|
||||
HeapNumber::kExponentShift,
|
||||
HeapNumber::kExponentBits);
|
||||
// Load dest with zero. We use this either for the final shift or
|
||||
// for the answer.
|
||||
mov(dest, Operand(0, RelocInfo::NONE));
|
||||
@ -1883,6 +1906,52 @@ void MacroAssembler::ConvertToInt32(Register source,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::EmitVFPTruncate(VFPRoundingMode rounding_mode,
|
||||
SwVfpRegister result,
|
||||
DwVfpRegister double_input,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
CheckForInexactConversion check_inexact) {
|
||||
ASSERT(CpuFeatures::IsSupported(VFP3));
|
||||
CpuFeatures::Scope scope(VFP3);
|
||||
Register prev_fpscr = scratch1;
|
||||
Register scratch = scratch2;
|
||||
|
||||
int32_t check_inexact_conversion =
|
||||
(check_inexact == kCheckForInexactConversion) ? kVFPInexactExceptionBit : 0;
|
||||
|
||||
// Set custom FPCSR:
|
||||
// - 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 |
|
||||
check_inexact_conversion |
|
||||
kVFPRoundingModeMask |
|
||||
kVFPFlushToZeroMask));
|
||||
// 'Round To Nearest' is encoded by 0b00 so no bits need to be set.
|
||||
if (rounding_mode != kRoundToNearest) {
|
||||
orr(scratch, scratch, Operand(rounding_mode));
|
||||
}
|
||||
vmsr(scratch);
|
||||
|
||||
// Convert the argument to an integer.
|
||||
vcvt_s32_f64(result,
|
||||
double_input,
|
||||
(rounding_mode == kRoundToZero) ? kDefaultRoundToZero
|
||||
: kFPSCRRounding);
|
||||
|
||||
// Retrieve FPSCR.
|
||||
vmrs(scratch);
|
||||
// Restore FPSCR.
|
||||
vmsr(prev_fpscr);
|
||||
// Check for vfp exceptions.
|
||||
tst(scratch, Operand(kVFPExceptionMask | check_inexact_conversion));
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::GetLeastBitsFromSmi(Register dst,
|
||||
Register src,
|
||||
int num_least_bits) {
|
||||
|
@ -121,6 +121,15 @@ class MacroAssembler: public Assembler {
|
||||
Condition cond = al);
|
||||
void Sbfx(Register dst, Register src, int lsb, int width,
|
||||
Condition cond = al);
|
||||
// The scratch register is not used for ARMv7.
|
||||
// scratch can be the same register as src (in which case it is trashed), but
|
||||
// not the same as dst.
|
||||
void Bfi(Register dst,
|
||||
Register src,
|
||||
Register scratch,
|
||||
int lsb,
|
||||
int width,
|
||||
Condition cond = al);
|
||||
void Bfc(Register dst, int lsb, int width, Condition cond = al);
|
||||
void Usat(Register dst, int satpos, const Operand& src,
|
||||
Condition cond = al);
|
||||
@ -234,6 +243,17 @@ class MacroAssembler: public Assembler {
|
||||
}
|
||||
}
|
||||
|
||||
// Pop two registers. Pops rightmost register first (from lower address).
|
||||
void Pop(Register src1, Register src2, Condition cond = al) {
|
||||
ASSERT(!src1.is(src2));
|
||||
if (src1.code() > src2.code()) {
|
||||
ldm(ia_w, sp, src1.bit() | src2.bit(), cond);
|
||||
} else {
|
||||
ldr(src2, MemOperand(sp, 4, PostIndex), cond);
|
||||
ldr(src1, MemOperand(sp, 4, PostIndex), cond);
|
||||
}
|
||||
}
|
||||
|
||||
// Push and pop the registers that can hold pointers, as defined by the
|
||||
// RegList constant kSafepointSavedRegisters.
|
||||
void PushSafepointRegisters();
|
||||
@ -621,6 +641,19 @@ class MacroAssembler: public Assembler {
|
||||
DwVfpRegister double_scratch,
|
||||
Label *not_int32);
|
||||
|
||||
// Truncates a double using a specific rounding mode.
|
||||
// Clears the z flag (ne condition) if an overflow occurs.
|
||||
// If exact_conversion is true, the z flag is also cleared if the conversion
|
||||
// was inexact, ie. if the double value could not be converted exactly
|
||||
// to a 32bit integer.
|
||||
void EmitVFPTruncate(VFPRoundingMode rounding_mode,
|
||||
SwVfpRegister result,
|
||||
DwVfpRegister double_input,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
CheckForInexactConversion check
|
||||
= kDontCheckForInexactConversion);
|
||||
|
||||
// Count leading zeros in a 32 bit word. On ARM5 and later it uses the clz
|
||||
// instruction. On pre-ARM5 hardware this routine gives the wrong answer
|
||||
// for 0 (31 instead of 32). Source and scratch can be the same in which case
|
||||
|
@ -2564,6 +2564,7 @@ void Simulator::DecodeTypeVFP(Instruction* instr) {
|
||||
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);
|
||||
set_d_register_from_double(vd, dd_value);
|
||||
} else {
|
||||
UNIMPLEMENTED(); // Not used by V8.
|
||||
@ -2798,14 +2799,17 @@ void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) {
|
||||
|
||||
inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer);
|
||||
|
||||
double abs_diff =
|
||||
unsigned_integer ? fabs(val - static_cast<uint32_t>(temp))
|
||||
: fabs(val - temp);
|
||||
|
||||
inexact_vfp_flag_ = (abs_diff != 0);
|
||||
|
||||
if (inv_op_vfp_flag_) {
|
||||
temp = VFPConversionSaturate(val, unsigned_integer);
|
||||
} else {
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user