Implement truncated d-to-i as a stub on x86
- Added a general DoubleToIStub so that it's possible to extend to other platforms and non-truncating case. - This version handles all cases of truncation (previous code deopted in some cases) and all source/destination register combinations without clobbering any temps. R=yangguo@chromium.org Review URL: https://codereview.chromium.org/18612005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15645 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
4780c99790
commit
48b65f8cd5
@ -72,6 +72,7 @@ namespace internal {
|
||||
V(ArgumentsAccess) \
|
||||
V(RegExpConstructResult) \
|
||||
V(NumberToString) \
|
||||
V(DoubleToI) \
|
||||
V(CEntry) \
|
||||
V(JSEntry) \
|
||||
V(KeyedLoadElement) \
|
||||
@ -1746,6 +1747,60 @@ class KeyedLoadDictionaryElementStub : public PlatformCodeStub {
|
||||
};
|
||||
|
||||
|
||||
class DoubleToIStub : public PlatformCodeStub {
|
||||
public:
|
||||
DoubleToIStub(Register source,
|
||||
Register destination,
|
||||
int offset,
|
||||
bool is_truncating) : bit_field_(0) {
|
||||
bit_field_ = SourceRegisterBits::encode(source.code_) |
|
||||
DestinationRegisterBits::encode(destination.code_) |
|
||||
OffsetBits::encode(offset) |
|
||||
IsTruncatingBits::encode(is_truncating);
|
||||
}
|
||||
|
||||
Register source() {
|
||||
Register result = { SourceRegisterBits::decode(bit_field_) };
|
||||
return result;
|
||||
}
|
||||
|
||||
Register destination() {
|
||||
Register result = { DestinationRegisterBits::decode(bit_field_) };
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_truncating() {
|
||||
return IsTruncatingBits::decode(bit_field_);
|
||||
}
|
||||
|
||||
int offset() {
|
||||
return OffsetBits::decode(bit_field_);
|
||||
}
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
private:
|
||||
static const int kBitsPerRegisterNumber = 6;
|
||||
STATIC_ASSERT((1L << kBitsPerRegisterNumber) >= Register::kNumRegisters);
|
||||
class SourceRegisterBits:
|
||||
public BitField<int, 0, kBitsPerRegisterNumber> {}; // NOLINT
|
||||
class DestinationRegisterBits:
|
||||
public BitField<int, kBitsPerRegisterNumber,
|
||||
kBitsPerRegisterNumber> {}; // NOLINT
|
||||
class IsTruncatingBits:
|
||||
public BitField<bool, 2 * kBitsPerRegisterNumber, 1> {}; // NOLINT
|
||||
class OffsetBits:
|
||||
public BitField<int, 2 * kBitsPerRegisterNumber + 1, 3> {}; // NOLINT
|
||||
|
||||
Major MajorKey() { return DoubleToI; }
|
||||
int MinorKey() { return bit_field_; }
|
||||
|
||||
int bit_field_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DoubleToIStub);
|
||||
};
|
||||
|
||||
|
||||
class KeyedLoadFastElementStub : public HydrogenCodeStub {
|
||||
public:
|
||||
KeyedLoadFastElementStub(bool is_js_array, ElementsKind elements_kind) {
|
||||
|
@ -662,131 +662,143 @@ class FloatingPointHelper : public AllStatic {
|
||||
};
|
||||
|
||||
|
||||
// Get the integer part of a heap number. Surprisingly, all this bit twiddling
|
||||
// is faster than using the built-in instructions on floating point registers.
|
||||
// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
|
||||
// trashed registers.
|
||||
static void IntegerConvert(MacroAssembler* masm,
|
||||
Register source,
|
||||
bool use_sse3,
|
||||
Label* conversion_failure) {
|
||||
ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
|
||||
Label done, right_exponent, normal_exponent;
|
||||
Register scratch = ebx;
|
||||
Register scratch2 = edi;
|
||||
// Get exponent word.
|
||||
__ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
|
||||
// Get exponent alone in scratch2.
|
||||
__ mov(scratch2, scratch);
|
||||
__ and_(scratch2, HeapNumber::kExponentMask);
|
||||
__ shr(scratch2, HeapNumber::kExponentShift);
|
||||
__ sub(scratch2, Immediate(HeapNumber::kExponentBias));
|
||||
// Load ecx with zero. We use this either for the final shift or
|
||||
// for the answer.
|
||||
__ xor_(ecx, ecx);
|
||||
// If the exponent is above 83, the number contains no significant
|
||||
// bits in the range 0..2^31, so the result is zero.
|
||||
static const uint32_t kResultIsZeroExponent = 83;
|
||||
__ cmp(scratch2, Immediate(kResultIsZeroExponent));
|
||||
__ j(above, &done);
|
||||
if (use_sse3) {
|
||||
void DoubleToIStub::Generate(MacroAssembler* masm) {
|
||||
Register input_reg = this->source();
|
||||
Register final_result_reg = this->destination();
|
||||
ASSERT(is_truncating());
|
||||
|
||||
Label check_negative, process_64_bits, done, done_no_stash;
|
||||
|
||||
int double_offset = offset();
|
||||
|
||||
// Account for return address and saved regs if input is esp.
|
||||
if (input_reg.is(esp)) double_offset += 3 * kPointerSize;
|
||||
|
||||
MemOperand mantissa_operand(MemOperand(input_reg, double_offset));
|
||||
MemOperand exponent_operand(MemOperand(input_reg,
|
||||
double_offset + kPointerSize));
|
||||
|
||||
Register scratch1;
|
||||
{
|
||||
Register scratch_candidates[3] = { ebx, edx, edi };
|
||||
for (int i = 0; i < 3; i++) {
|
||||
scratch1 = scratch_candidates[i];
|
||||
if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break;
|
||||
}
|
||||
}
|
||||
// Since we must use ecx for shifts below, use some other register (eax)
|
||||
// to calculate the result if ecx is the requested return register.
|
||||
Register result_reg = final_result_reg.is(ecx) ? eax : final_result_reg;
|
||||
// Save ecx if it isn't the return register and therefore volatile, or if it
|
||||
// is the return register, then save the temp register we use in its stead for
|
||||
// the result.
|
||||
Register save_reg = final_result_reg.is(ecx) ? eax : ecx;
|
||||
__ push(scratch1);
|
||||
__ push(save_reg);
|
||||
|
||||
bool stash_exponent_copy = !input_reg.is(esp);
|
||||
__ mov(scratch1, mantissa_operand);
|
||||
if (CpuFeatures::IsSupported(SSE3)) {
|
||||
CpuFeatureScope scope(masm, SSE3);
|
||||
// Check whether the exponent is too big for a 64 bit signed integer.
|
||||
static const uint32_t kTooBigExponent = 63;
|
||||
__ cmp(scratch2, Immediate(kTooBigExponent));
|
||||
__ j(greater_equal, conversion_failure);
|
||||
// Load x87 register with heap number.
|
||||
__ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
|
||||
__ fld_d(mantissa_operand);
|
||||
}
|
||||
__ mov(ecx, exponent_operand);
|
||||
if (stash_exponent_copy) __ push(ecx);
|
||||
|
||||
__ and_(ecx, HeapNumber::kExponentMask);
|
||||
__ shr(ecx, HeapNumber::kExponentShift);
|
||||
__ lea(result_reg, MemOperand(ecx, -HeapNumber::kExponentBias));
|
||||
__ cmp(result_reg, Immediate(HeapNumber::kMantissaBits));
|
||||
__ j(below, &process_64_bits);
|
||||
|
||||
// Result is entirely in lower 32-bits of mantissa
|
||||
int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize;
|
||||
if (CpuFeatures::IsSupported(SSE3)) {
|
||||
__ fstp(0);
|
||||
}
|
||||
__ sub(ecx, Immediate(delta));
|
||||
__ xor_(result_reg, result_reg);
|
||||
__ cmp(ecx, Immediate(31));
|
||||
__ j(above, &done);
|
||||
__ shl_cl(scratch1);
|
||||
__ jmp(&check_negative);
|
||||
|
||||
__ bind(&process_64_bits);
|
||||
if (CpuFeatures::IsSupported(SSE3)) {
|
||||
CpuFeatureScope scope(masm, SSE3);
|
||||
if (stash_exponent_copy) {
|
||||
// Already a copy of the exponent on the stack, overwrite it.
|
||||
STATIC_ASSERT(kDoubleSize == 2 * kPointerSize);
|
||||
__ sub(esp, Immediate(kDoubleSize / 2));
|
||||
} else {
|
||||
// Reserve space for 64 bit answer.
|
||||
__ sub(esp, Immediate(sizeof(uint64_t))); // Nolint.
|
||||
__ sub(esp, Immediate(kDoubleSize)); // Nolint.
|
||||
}
|
||||
// Do conversion, which cannot fail because we checked the exponent.
|
||||
__ fisttp_d(Operand(esp, 0));
|
||||
__ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
|
||||
__ add(esp, Immediate(sizeof(uint64_t))); // Nolint.
|
||||
__ mov(result_reg, Operand(esp, 0)); // Load low word of answer as result
|
||||
__ add(esp, Immediate(kDoubleSize));
|
||||
__ jmp(&done_no_stash);
|
||||
} else {
|
||||
// Check whether the exponent matches a 32 bit signed int that cannot be
|
||||
// represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
|
||||
// exponent is 30 (biased). This is the exponent that we are fastest at and
|
||||
// also the highest exponent we can handle here.
|
||||
const uint32_t non_smi_exponent = 30;
|
||||
__ cmp(scratch2, Immediate(non_smi_exponent));
|
||||
// If we have a match of the int32-but-not-Smi exponent then skip some
|
||||
// logic.
|
||||
__ j(equal, &right_exponent, Label::kNear);
|
||||
// If the exponent is higher than that then go to slow case. This catches
|
||||
// numbers that don't fit in a signed int32, infinities and NaNs.
|
||||
__ j(less, &normal_exponent, Label::kNear);
|
||||
|
||||
{
|
||||
// Handle a big exponent. The only reason we have this code is that the
|
||||
// >>> operator has a tendency to generate numbers with an exponent of 31.
|
||||
const uint32_t big_non_smi_exponent = 31;
|
||||
__ cmp(scratch2, Immediate(big_non_smi_exponent));
|
||||
__ j(not_equal, conversion_failure);
|
||||
// We have the big exponent, typically from >>>. This means the number is
|
||||
// in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
|
||||
__ mov(scratch2, scratch);
|
||||
__ and_(scratch2, HeapNumber::kMantissaMask);
|
||||
// Put back the implicit 1.
|
||||
__ or_(scratch2, 1 << HeapNumber::kExponentShift);
|
||||
// Shift up the mantissa bits to take up the space the exponent used to
|
||||
// take. We just orred in the implicit bit so that took care of one and
|
||||
// we want to use the full unsigned range so we subtract 1 bit from the
|
||||
// shift distance.
|
||||
const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
|
||||
__ shl(scratch2, big_shift_distance);
|
||||
// Get the second half of the double.
|
||||
__ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
|
||||
// Shift down 21 bits to get the most significant 11 bits or the low
|
||||
// mantissa word.
|
||||
__ shr(ecx, 32 - big_shift_distance);
|
||||
__ or_(ecx, scratch2);
|
||||
// We have the answer in ecx, but we may need to negate it.
|
||||
__ test(scratch, scratch);
|
||||
__ j(positive, &done, Label::kNear);
|
||||
// Result must be extracted from shifted 32-bit mantissa
|
||||
__ sub(ecx, Immediate(delta));
|
||||
__ neg(ecx);
|
||||
__ jmp(&done, Label::kNear);
|
||||
if (stash_exponent_copy) {
|
||||
__ mov(result_reg, MemOperand(esp, 0));
|
||||
} else {
|
||||
__ mov(result_reg, exponent_operand);
|
||||
}
|
||||
__ and_(result_reg,
|
||||
Immediate(static_cast<uint32_t>(Double::kSignificandMask >> 32)));
|
||||
__ add(result_reg,
|
||||
Immediate(static_cast<uint32_t>(Double::kHiddenBit >> 32)));
|
||||
__ shrd(result_reg, scratch1);
|
||||
__ shr_cl(result_reg);
|
||||
__ test(ecx, Immediate(32));
|
||||
if (CpuFeatures::IsSupported(CMOV)) {
|
||||
CpuFeatureScope use_cmov(masm, CMOV);
|
||||
__ cmov(not_equal, scratch1, result_reg);
|
||||
} else {
|
||||
Label skip_mov;
|
||||
__ j(equal, &skip_mov, Label::kNear);
|
||||
__ mov(scratch1, result_reg);
|
||||
__ bind(&skip_mov);
|
||||
}
|
||||
}
|
||||
|
||||
__ bind(&normal_exponent);
|
||||
// Exponent word in scratch, exponent in scratch2. Zero in ecx.
|
||||
// We know that 0 <= exponent < 30.
|
||||
__ mov(ecx, Immediate(30));
|
||||
__ sub(ecx, scratch2);
|
||||
|
||||
__ bind(&right_exponent);
|
||||
// Here ecx is the shift, scratch is the exponent word.
|
||||
// Get the top bits of the mantissa.
|
||||
__ and_(scratch, HeapNumber::kMantissaMask);
|
||||
// Put back the implicit 1.
|
||||
__ or_(scratch, 1 << HeapNumber::kExponentShift);
|
||||
// Shift up the mantissa bits to take up the space the exponent used to
|
||||
// take. We have kExponentShift + 1 significant bits int he low end of the
|
||||
// word. Shift them to the top bits.
|
||||
const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
|
||||
__ shl(scratch, shift_distance);
|
||||
// Get the second half of the double. For some exponents we don't
|
||||
// actually need this because the bits get shifted out again, but
|
||||
// it's probably slower to test than just to do it.
|
||||
__ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
|
||||
// Shift down 22 bits to get the most significant 10 bits or the low
|
||||
// mantissa word.
|
||||
__ shr(scratch2, 32 - shift_distance);
|
||||
__ or_(scratch2, scratch);
|
||||
// Move down according to the exponent.
|
||||
__ shr_cl(scratch2);
|
||||
// Now the unsigned answer is in scratch2. We need to move it to ecx and
|
||||
// we may need to fix the sign.
|
||||
Label negative;
|
||||
__ xor_(ecx, ecx);
|
||||
__ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
|
||||
__ j(greater, &negative, Label::kNear);
|
||||
__ mov(ecx, scratch2);
|
||||
__ jmp(&done, Label::kNear);
|
||||
__ bind(&negative);
|
||||
__ sub(ecx, scratch2);
|
||||
// If the double was negative, negate the integer result.
|
||||
__ bind(&check_negative);
|
||||
__ mov(result_reg, scratch1);
|
||||
__ neg(result_reg);
|
||||
if (stash_exponent_copy) {
|
||||
__ cmp(MemOperand(esp, 0), Immediate(0));
|
||||
} else {
|
||||
__ cmp(exponent_operand, Immediate(0));
|
||||
}
|
||||
if (CpuFeatures::IsSupported(CMOV)) {
|
||||
CpuFeatureScope use_cmov(masm, CMOV);
|
||||
__ cmov(greater, result_reg, scratch1);
|
||||
} else {
|
||||
Label skip_mov;
|
||||
__ j(less_equal, &skip_mov, Label::kNear);
|
||||
__ mov(result_reg, scratch1);
|
||||
__ bind(&skip_mov);
|
||||
}
|
||||
|
||||
// Restore registers
|
||||
__ bind(&done);
|
||||
if (stash_exponent_copy) {
|
||||
__ add(esp, Immediate(kDoubleSize / 2));
|
||||
}
|
||||
__ bind(&done_no_stash);
|
||||
if (!final_result_reg.is(result_reg)) {
|
||||
ASSERT(final_result_reg.is(ecx));
|
||||
__ mov(final_result_reg, result_reg);
|
||||
}
|
||||
__ pop(save_reg);
|
||||
__ pop(scratch1);
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
|
||||
@ -2407,7 +2419,9 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(
|
||||
CpuFeatureScope use_sse2(masm, SSE2);
|
||||
ConvertHeapNumberToInt32(masm, edx, conversion_failure);
|
||||
} else {
|
||||
IntegerConvert(masm, edx, use_sse3, conversion_failure);
|
||||
DoubleToIStub stub(edx, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
|
||||
true);
|
||||
__ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
|
||||
}
|
||||
__ mov(edx, ecx);
|
||||
|
||||
@ -2442,7 +2456,9 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(
|
||||
CpuFeatureScope use_sse2(masm, SSE2);
|
||||
ConvertHeapNumberToInt32(masm, eax, conversion_failure);
|
||||
} else {
|
||||
IntegerConvert(masm, eax, use_sse3, conversion_failure);
|
||||
DoubleToIStub stub(eax, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
|
||||
true);
|
||||
__ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
__ bind(&done);
|
||||
|
@ -5645,93 +5645,22 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
|
||||
XMMRegister input_reg = ToDoubleRegister(input);
|
||||
Register result_reg = ToRegister(result);
|
||||
|
||||
__ cvttsd2si(result_reg, Operand(input_reg));
|
||||
|
||||
if (instr->truncating()) {
|
||||
// Performs a truncating conversion of a floating point number as used by
|
||||
// the JS bitwise operations.
|
||||
__ cvttsd2si(result_reg, Operand(input_reg));
|
||||
Label fast_case_succeeded;
|
||||
__ cmp(result_reg, 0x80000000u);
|
||||
if (CpuFeatures::IsSupported(SSE3)) {
|
||||
// This will deoptimize if the exponent of the input in out of range.
|
||||
CpuFeatureScope scope(masm(), SSE3);
|
||||
Label convert, done;
|
||||
__ j(not_equal, &done, Label::kNear);
|
||||
__ sub(Operand(esp), Immediate(kDoubleSize));
|
||||
__ movdbl(Operand(esp, 0), input_reg);
|
||||
// Get exponent alone and check for too-big exponent.
|
||||
__ mov(result_reg, Operand(esp, sizeof(int32_t)));
|
||||
__ and_(result_reg, HeapNumber::kExponentMask);
|
||||
const uint32_t kTooBigExponent =
|
||||
(HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
|
||||
__ cmp(Operand(result_reg), Immediate(kTooBigExponent));
|
||||
__ j(less, &convert, Label::kNear);
|
||||
__ add(Operand(esp), Immediate(kDoubleSize));
|
||||
DeoptimizeIf(no_condition, instr->environment());
|
||||
__ bind(&convert);
|
||||
// Do conversion, which cannot fail because we checked the exponent.
|
||||
__ fld_d(Operand(esp, 0));
|
||||
__ fisttp_d(Operand(esp, 0));
|
||||
__ mov(result_reg, Operand(esp, 0)); // Low word of answer is the result.
|
||||
__ add(Operand(esp), Immediate(kDoubleSize));
|
||||
__ bind(&done);
|
||||
__ j(not_equal, &fast_case_succeeded);
|
||||
__ sub(esp, Immediate(kDoubleSize));
|
||||
__ movdbl(MemOperand(esp, 0), input_reg);
|
||||
DoubleToIStub stub(esp, result_reg, 0, true);
|
||||
__ call(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
|
||||
__ add(esp, Immediate(kDoubleSize));
|
||||
__ bind(&fast_case_succeeded);
|
||||
} else {
|
||||
Label done;
|
||||
Register temp_reg = ToRegister(instr->temp());
|
||||
XMMRegister xmm_scratch = xmm0;
|
||||
|
||||
// If cvttsd2si succeeded, we're done. Otherwise, we attempt
|
||||
// manual conversion.
|
||||
__ j(not_equal, &done, Label::kNear);
|
||||
|
||||
// Get high 32 bits of the input in result_reg and temp_reg.
|
||||
__ pshufd(xmm_scratch, input_reg, 1);
|
||||
__ movd(Operand(temp_reg), xmm_scratch);
|
||||
__ mov(result_reg, temp_reg);
|
||||
|
||||
// Prepare negation mask in temp_reg.
|
||||
__ sar(temp_reg, kBitsPerInt - 1);
|
||||
|
||||
// Extract the exponent from result_reg and subtract adjusted
|
||||
// bias from it. The adjustment is selected in a way such that
|
||||
// when the difference is zero, the answer is in the low 32 bits
|
||||
// of the input, otherwise a shift has to be performed.
|
||||
__ shr(result_reg, HeapNumber::kExponentShift);
|
||||
__ and_(result_reg,
|
||||
HeapNumber::kExponentMask >> HeapNumber::kExponentShift);
|
||||
__ sub(Operand(result_reg),
|
||||
Immediate(HeapNumber::kExponentBias +
|
||||
HeapNumber::kExponentBits +
|
||||
HeapNumber::kMantissaBits));
|
||||
// Don't handle big (> kMantissaBits + kExponentBits == 63) or
|
||||
// special exponents.
|
||||
DeoptimizeIf(greater, instr->environment());
|
||||
|
||||
// Zero out the sign and the exponent in the input (by shifting
|
||||
// it to the left) and restore the implicit mantissa bit,
|
||||
// i.e. convert the input to unsigned int64 shifted left by
|
||||
// kExponentBits.
|
||||
ExternalReference minus_zero = ExternalReference::address_of_minus_zero();
|
||||
// Minus zero has the most significant bit set and the other
|
||||
// bits cleared.
|
||||
__ movdbl(xmm_scratch, Operand::StaticVariable(minus_zero));
|
||||
__ psllq(input_reg, HeapNumber::kExponentBits);
|
||||
__ por(input_reg, xmm_scratch);
|
||||
|
||||
// Get the amount to shift the input right in xmm_scratch.
|
||||
__ neg(result_reg);
|
||||
__ movd(xmm_scratch, Operand(result_reg));
|
||||
|
||||
// Shift the input right and extract low 32 bits.
|
||||
__ psrlq(input_reg, xmm_scratch);
|
||||
__ movd(Operand(result_reg), input_reg);
|
||||
|
||||
// Use the prepared mask in temp_reg to negate the result if necessary.
|
||||
__ xor_(result_reg, Operand(temp_reg));
|
||||
__ sub(result_reg, Operand(temp_reg));
|
||||
__ bind(&done);
|
||||
}
|
||||
} else {
|
||||
Label done;
|
||||
__ cvttsd2si(result_reg, Operand(input_reg));
|
||||
__ cvtsi2sd(xmm0, Operand(result_reg));
|
||||
__ ucomisd(xmm0, input_reg);
|
||||
DeoptimizeIf(not_equal, instr->environment());
|
||||
|
@ -110,6 +110,7 @@
|
||||
['v8_target_arch=="ia32"', {
|
||||
'sources': [
|
||||
'test-assembler-ia32.cc',
|
||||
'test-code-stubs-ia32.cc',
|
||||
'test-disasm-ia32.cc',
|
||||
'test-log-stack-tracer.cc'
|
||||
],
|
||||
|
274
test/cctest/test-code-stubs-ia32.cc
Normal file
274
test/cctest/test-code-stubs-ia32.cc
Normal file
@ -0,0 +1,274 @@
|
||||
// Copyright 2013 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "cctest.h"
|
||||
#include "code-stubs.h"
|
||||
#include "factory.h"
|
||||
#include "macro-assembler.h"
|
||||
#include "platform.h"
|
||||
|
||||
#if __GNUC__
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#else
|
||||
#define STDCALL __stdcall
|
||||
#endif
|
||||
|
||||
using namespace v8::internal;
|
||||
|
||||
|
||||
typedef int32_t STDCALL (*ConvertDToIFunc)(double input);
|
||||
|
||||
|
||||
int STDCALL ConvertDToICVersion(double d) {
|
||||
Address double_ptr = reinterpret_cast<Address>(&d);
|
||||
uint32_t exponent_bits = Memory::uint32_at(double_ptr + kDoubleSize / 2);
|
||||
int32_t shifted_mask = static_cast<int32_t>(Double::kExponentMask >> 32);
|
||||
int32_t exponent = (((exponent_bits & shifted_mask) >>
|
||||
(Double::kPhysicalSignificandSize - 32)) -
|
||||
HeapNumber::kExponentBias);
|
||||
uint32_t unsigned_exponent = static_cast<uint32_t>(exponent);
|
||||
int result = 0;
|
||||
uint32_t max_exponent =
|
||||
static_cast<uint32_t>(Double::kPhysicalSignificandSize);
|
||||
if (unsigned_exponent >= max_exponent) {
|
||||
if ((exponent - Double::kPhysicalSignificandSize) < 32) {
|
||||
result = Memory::uint32_at(double_ptr) <<
|
||||
(exponent - Double::kPhysicalSignificandSize);
|
||||
}
|
||||
} else {
|
||||
uint64_t big_result =
|
||||
(BitCast<uint64_t>(d) & Double::kSignificandMask) | Double::kHiddenBit;
|
||||
big_result = big_result >> (Double::kPhysicalSignificandSize - exponent);
|
||||
result = static_cast<uint32_t>(big_result);
|
||||
}
|
||||
if (static_cast<int32_t>(exponent_bits) < 0) {
|
||||
return (0 - result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RunOneTruncationTestWithTest(ConvertDToIFunc func,
|
||||
double from,
|
||||
int64_t to) {
|
||||
int result = (*func)(from);
|
||||
CHECK_EQ(static_cast<int>(to), result);
|
||||
}
|
||||
|
||||
|
||||
// #define NaN and Infinity so that it's possible to cut-and-paste these tests
|
||||
// directly to a .js file and run them.
|
||||
#define NaN NAN
|
||||
#define Infinity INFINITY
|
||||
#define RunOneTruncationTest(p1, p2) RunOneTruncationTestWithTest(func, p1, p2)
|
||||
|
||||
void RunAllTruncationTests(ConvertDToIFunc func) {
|
||||
RunOneTruncationTest(0, 0);
|
||||
RunOneTruncationTest(0.5, 0);
|
||||
RunOneTruncationTest(-0.5, 0);
|
||||
RunOneTruncationTest(1.5, 1);
|
||||
RunOneTruncationTest(-1.5, -1);
|
||||
RunOneTruncationTest(5.5, 5);
|
||||
RunOneTruncationTest(-5.0, -5);
|
||||
RunOneTruncationTest(NaN, 0);
|
||||
RunOneTruncationTest(Infinity, 0);
|
||||
RunOneTruncationTest(-NaN, 0);
|
||||
RunOneTruncationTest(-Infinity, 0);
|
||||
|
||||
RunOneTruncationTest(4.5036e+15, 0x1635E000);
|
||||
RunOneTruncationTest(-4.5036e+15, -372629504);
|
||||
|
||||
RunOneTruncationTest(4503603922337791.0, -1);
|
||||
RunOneTruncationTest(-4503603922337791.0, 1);
|
||||
RunOneTruncationTest(4503601774854143.0, 2147483647);
|
||||
RunOneTruncationTest(-4503601774854143.0, -2147483647);
|
||||
RunOneTruncationTest(9007207844675582.0, -2);
|
||||
RunOneTruncationTest(-9007207844675582.0, 2);
|
||||
RunOneTruncationTest(2.4178527921507624e+24, -536870912);
|
||||
RunOneTruncationTest(-2.4178527921507624e+24, 536870912);
|
||||
RunOneTruncationTest(2.417853945072267e+24, -536870912);
|
||||
RunOneTruncationTest(-2.417853945072267e+24, 536870912);
|
||||
|
||||
RunOneTruncationTest(4.8357055843015248e+24, -1073741824);
|
||||
RunOneTruncationTest(-4.8357055843015248e+24, 1073741824);
|
||||
RunOneTruncationTest(4.8357078901445341e+24, -1073741824);
|
||||
RunOneTruncationTest(-4.8357078901445341e+24, 1073741824);
|
||||
|
||||
RunOneTruncationTest(9.6714111686030497e+24, -2147483648.0);
|
||||
RunOneTruncationTest(-9.6714111686030497e+24, -2147483648.0);
|
||||
RunOneTruncationTest(9.6714157802890681e+24, -2147483648.0);
|
||||
RunOneTruncationTest(-9.6714157802890681e+24, -2147483648.0);
|
||||
}
|
||||
|
||||
#undef NaN
|
||||
#undef Infinity
|
||||
#undef RunOneTruncationTest
|
||||
|
||||
#define __ assm.
|
||||
|
||||
ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate,
|
||||
Register source_reg,
|
||||
Register destination_reg) {
|
||||
// Allocate an executable page of memory.
|
||||
size_t actual_size;
|
||||
byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
|
||||
&actual_size,
|
||||
true));
|
||||
CHECK(buffer);
|
||||
HandleScope handles(isolate);
|
||||
MacroAssembler assm(isolate, buffer, static_cast<int>(actual_size));
|
||||
assm.set_allow_stub_calls(false);
|
||||
int offset =
|
||||
source_reg.is(esp) ? 0 : (HeapNumber::kValueOffset - kSmiTagSize);
|
||||
DoubleToIStub stub(source_reg, destination_reg, offset, true);
|
||||
byte* start = stub.GetCode(isolate)->instruction_start();
|
||||
|
||||
__ push(ebx);
|
||||
__ push(ecx);
|
||||
__ push(edx);
|
||||
__ push(esi);
|
||||
__ push(edi);
|
||||
|
||||
if (!source_reg.is(esp)) {
|
||||
__ lea(source_reg, MemOperand(esp, 6 * kPointerSize - offset));
|
||||
}
|
||||
|
||||
int param_offset = 7 * kPointerSize;
|
||||
// Save registers make sure they don't get clobbered.
|
||||
int reg_num = 0;
|
||||
for (;reg_num < Register::NumAllocatableRegisters(); ++reg_num) {
|
||||
Register reg = Register::from_code(reg_num);
|
||||
if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) {
|
||||
__ push(reg);
|
||||
param_offset += kPointerSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-push the double argument
|
||||
__ push(MemOperand(esp, param_offset));
|
||||
__ push(MemOperand(esp, param_offset));
|
||||
|
||||
// Call through to the actual stub
|
||||
__ call(start, RelocInfo::EXTERNAL_REFERENCE);
|
||||
|
||||
__ add(esp, Immediate(kDoubleSize));
|
||||
|
||||
// Make sure no registers have been unexpectedly clobbered
|
||||
for (--reg_num; reg_num >= 0; --reg_num) {
|
||||
Register reg = Register::from_code(reg_num);
|
||||
if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) {
|
||||
__ cmp(reg, MemOperand(esp, 0));
|
||||
__ Assert(equal, "register was clobbered");
|
||||
__ add(esp, Immediate(kPointerSize));
|
||||
}
|
||||
}
|
||||
|
||||
__ mov(eax, destination_reg);
|
||||
|
||||
__ pop(edi);
|
||||
__ pop(esi);
|
||||
__ pop(edx);
|
||||
__ pop(ecx);
|
||||
__ pop(ebx);
|
||||
|
||||
__ ret(kDoubleSize);
|
||||
|
||||
CodeDesc desc;
|
||||
assm.GetCode(&desc);
|
||||
return reinterpret_cast<ConvertDToIFunc>(
|
||||
reinterpret_cast<intptr_t>(buffer));
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
static Isolate* GetIsolateFrom(LocalContext* context) {
|
||||
return reinterpret_cast<Isolate*>((*context)->GetIsolate());
|
||||
}
|
||||
|
||||
|
||||
TEST(ConvertDToI) {
|
||||
CcTest::InitializeVM();
|
||||
LocalContext context;
|
||||
Isolate* isolate = GetIsolateFrom(&context);
|
||||
HandleScope scope(isolate);
|
||||
|
||||
#if DEBUG
|
||||
// Verify that the tests actually work with the C version. In the release
|
||||
// code, the compiler optimizes it away because it's all constant, but does it
|
||||
// wrong, triggering an assert on gcc.
|
||||
RunAllTruncationTests(&ConvertDToICVersion);
|
||||
#endif
|
||||
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, esi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, esi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, esi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, esi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, esi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, esi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, eax));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, ebx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, ecx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, edx));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, edi));
|
||||
RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, esi));
|
||||
}
|
78
test/mjsunit/double-truncation.js
Normal file
78
test/mjsunit/double-truncation.js
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
function RunOneTruncationTest(a, b) {
|
||||
var temp = a | 0;
|
||||
assertEquals(b, temp);
|
||||
}
|
||||
|
||||
function RunAllTruncationTests() {
|
||||
RunOneTruncationTest(0, 0);
|
||||
RunOneTruncationTest(0.5, 0);
|
||||
RunOneTruncationTest(-0.5, 0);
|
||||
RunOneTruncationTest(1.5, 1);
|
||||
RunOneTruncationTest(-1.5, -1);
|
||||
RunOneTruncationTest(5.5, 5);
|
||||
RunOneTruncationTest(-5.0, -5);
|
||||
RunOneTruncationTest(NaN, 0);
|
||||
RunOneTruncationTest(Infinity, 0);
|
||||
RunOneTruncationTest(-NaN, 0);
|
||||
RunOneTruncationTest(-Infinity, 0);
|
||||
|
||||
RunOneTruncationTest(4.5036e+15, 0x1635E000);
|
||||
RunOneTruncationTest(-4.5036e+15, -372629504);
|
||||
|
||||
RunOneTruncationTest(4503603922337791.0, -1);
|
||||
RunOneTruncationTest(-4503603922337791.0, 1);
|
||||
RunOneTruncationTest(4503601774854143.0, 2147483647);
|
||||
RunOneTruncationTest(-4503601774854143.0, -2147483647);
|
||||
RunOneTruncationTest(9007207844675582.0, -2);
|
||||
RunOneTruncationTest(-9007207844675582.0, 2);
|
||||
|
||||
RunOneTruncationTest(2.4178527921507624e+24, -536870912);
|
||||
RunOneTruncationTest(-2.4178527921507624e+24, 536870912);
|
||||
RunOneTruncationTest(2.417853945072267e+24, -536870912);
|
||||
RunOneTruncationTest(-2.417853945072267e+24, 536870912);
|
||||
|
||||
RunOneTruncationTest(4.8357055843015248e+24, -1073741824);
|
||||
RunOneTruncationTest(-4.8357055843015248e+24, 1073741824);
|
||||
RunOneTruncationTest(4.8357078901445341e+24, -1073741824);
|
||||
RunOneTruncationTest(-4.8357078901445341e+24, 1073741824);
|
||||
|
||||
RunOneTruncationTest(9.6714111686030497e+24, -2147483648);
|
||||
RunOneTruncationTest(-9.6714111686030497e+24, -2147483648);
|
||||
RunOneTruncationTest(9.6714157802890681e+24, -2147483648);
|
||||
RunOneTruncationTest(-9.6714157802890681e+24, -2147483648);
|
||||
}
|
||||
|
||||
RunAllTruncationTests();
|
||||
RunAllTruncationTests();
|
||||
%OptimizeFunctionOnNextCall(RunOneTruncationTest);
|
||||
RunAllTruncationTests();
|
||||
RunAllTruncationTests();
|
Loading…
Reference in New Issue
Block a user