Commit 78df13d0 authored by olivf@chromium.org's avatar olivf@chromium.org

Move ToI conversions to the MacroAssembler

+ Replace DeferredTaggedToINoSSE2 by DoubleToIStub and a fpu version.

+ Prevent truncating TaggedToI from bailing out.

BUG=
R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/22290005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16464 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f4e16f24
......@@ -947,6 +947,11 @@ class HValue : public ZoneObject {
return type().ToStringOrToNumberCanBeObserved(representation());
}
MinusZeroMode GetMinusZeroMode() {
return CheckFlag(kBailoutOnMinusZero)
? FAIL_ON_MINUS_ZERO : TREAT_MINUS_ZERO_AS_ZERO;
}
protected:
// This function must be overridden for instructions with flag kUseGVN, to
// compare the non-Operand parts of the instruction.
......
......@@ -183,6 +183,7 @@ const IntelDoubleRegister double_register_4 = { 4 };
const IntelDoubleRegister double_register_5 = { 5 };
const IntelDoubleRegister double_register_6 = { 6 };
const IntelDoubleRegister double_register_7 = { 7 };
const IntelDoubleRegister no_double_reg = { -1 };
struct XMMRegister : IntelDoubleRegister {
......@@ -227,6 +228,7 @@ struct XMMRegister : IntelDoubleRegister {
#define xmm5 (static_cast<const XMMRegister&>(double_register_5))
#define xmm6 (static_cast<const XMMRegister&>(double_register_6))
#define xmm7 (static_cast<const XMMRegister&>(double_register_7))
#define no_xmm_reg (static_cast<const XMMRegister&>(no_double_reg))
struct X87Register : IntelDoubleRegister {
......
......@@ -658,18 +658,6 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
}
// Uses SSE2 to convert the heap number in |source| to an integer. Jumps to
// |conversion_failure| if the heap number did not contain an int32 value.
// Result is in ecx. Trashes ebx, xmm0, and xmm1.
static void ConvertHeapNumberToInt32(MacroAssembler* masm,
Register source,
Label* conversion_failure) {
__ movdbl(xmm0, FieldOperand(source, HeapNumber::kValueOffset));
FloatingPointHelper::CheckSSE2OperandIsInt32(
masm, conversion_failure, xmm0, ecx, ebx, xmm1);
}
void BinaryOpStub::Initialize() {
platform_specific_bit_ = CpuFeatures::IsSupported(SSE3);
}
......@@ -2270,16 +2258,7 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(
__ cmp(ebx, factory->heap_number_map());
__ j(not_equal, &check_undefined_arg1);
// Get the untagged integer version of the edx heap number in ecx.
if (left_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope use_sse2(masm, SSE2);
ConvertHeapNumberToInt32(masm, edx, conversion_failure);
} else {
DoubleToIStub stub(edx, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
true);
__ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
}
__ mov(edx, ecx);
__ TruncateHeapNumberToI(edx, edx);
// Here edx has the untagged integer, eax has a Smi or a heap number.
__ bind(&load_arg2);
......@@ -2308,14 +2287,7 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(
__ j(not_equal, &check_undefined_arg2);
// Get the untagged integer version of the eax heap number in ecx.
if (right_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope use_sse2(masm, SSE2);
ConvertHeapNumberToInt32(masm, eax, conversion_failure);
} else {
DoubleToIStub stub(eax, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
true);
__ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
}
__ TruncateHeapNumberToI(ecx, eax);
__ bind(&done);
__ mov(eax, edx);
......@@ -2542,16 +2514,16 @@ void MathPowStub::Generate(MacroAssembler* masm) {
}
if (exponent_type_ != INTEGER) {
Label fast_power;
// Detect integer exponents stored as double.
__ cvttsd2si(exponent, Operand(double_exponent));
Label fast_power, try_arithmetic_simplification;
__ DoubleToI(exponent, double_exponent, double_scratch,
TREAT_MINUS_ZERO_AS_ZERO, &try_arithmetic_simplification);
__ jmp(&int_exponent);
__ bind(&try_arithmetic_simplification);
// Skip to runtime if possibly NaN (indicated by the indefinite integer).
__ cvttsd2si(exponent, Operand(double_exponent));
__ cmp(exponent, Immediate(0x80000000u));
__ j(equal, &call_runtime);
__ cvtsi2sd(double_scratch, exponent);
// Already ruled out NaNs for exponent.
__ ucomisd(double_exponent, double_scratch);
__ j(equal, &int_exponent);
if (exponent_type_ == ON_STACK) {
// Detect square root case. Crankshaft detects constant +/-0.5 at
......
......@@ -448,6 +448,7 @@ bool LCodeGen::GenerateDeferredCode() {
Comment(";;; Deferred code");
}
code->Generate();
__ bind(code->done());
if (NeedsDeferredFrame()) {
Comment(";;; Destroy frame");
ASSERT(frame_is_built_);
......@@ -5306,94 +5307,39 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
}
void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
Label done, heap_number;
void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, Label* done) {
Register input_reg = ToRegister(instr->value());
// Heap number map check.
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
factory()->heap_number_map());
if (instr->truncating()) {
Label heap_number, slow_case;
// Heap number map check.
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
factory()->heap_number_map());
__ j(equal, &heap_number, Label::kNear);
// Check for undefined. Undefined is converted to zero for truncating
// conversions.
__ cmp(input_reg, factory()->undefined_value());
__ RecordComment("Deferred TaggedToI: cannot truncate");
DeoptimizeIf(not_equal, instr->environment());
__ mov(input_reg, 0);
__ jmp(&done, Label::kNear);
__ jmp(done);
__ bind(&heap_number);
if (CpuFeatures::IsSupported(SSE3)) {
CpuFeatureScope scope(masm(), SSE3);
Label convert;
// Use more powerful conversion when sse3 is available.
// Load x87 register with heap number.
__ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
// Get exponent alone and check for too-big exponent.
__ mov(input_reg, FieldOperand(input_reg, HeapNumber::kExponentOffset));
__ and_(input_reg, HeapNumber::kExponentMask);
const uint32_t kTooBigExponent =
(HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
__ cmp(Operand(input_reg), Immediate(kTooBigExponent));
__ j(less, &convert, Label::kNear);
// Pop FPU stack before deoptimizing.
__ fstp(0);
__ RecordComment("Deferred TaggedToI: exponent too big");
DeoptimizeIf(no_condition, instr->environment());
// Reserve space for 64 bit answer.
__ bind(&convert);
__ sub(Operand(esp), Immediate(kDoubleSize));
// Do conversion, which cannot fail because we checked the exponent.
__ fisttp_d(Operand(esp, 0));
__ mov(input_reg, Operand(esp, 0)); // Low word of answer is the result.
__ add(Operand(esp), Immediate(kDoubleSize));
} else if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
__ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
__ cvttsd2si(input_reg, Operand(xmm0));
__ cmp(input_reg, 0x80000000u);
__ j(not_equal, &done);
// Check if the input was 0x8000000 (kMinInt).
// If no, then we got an overflow and we deoptimize.
ExternalReference min_int = ExternalReference::address_of_min_int();
__ movdbl(xmm_temp, Operand::StaticVariable(min_int));
__ ucomisd(xmm_temp, xmm0);
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
} else {
UNREACHABLE();
}
} else if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
// Deoptimize if we don't have a heap number.
__ RecordComment("Deferred TaggedToI: not a heap number");
DeoptimizeIf(not_equal, instr->environment());
XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
__ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
__ cvttsd2si(input_reg, Operand(xmm0));
__ cvtsi2sd(xmm_temp, Operand(input_reg));
__ ucomisd(xmm0, xmm_temp);
__ RecordComment("Deferred TaggedToI: lost precision");
DeoptimizeIf(not_equal, instr->environment());
__ RecordComment("Deferred TaggedToI: NaN");
DeoptimizeIf(parity_even, instr->environment()); // NaN.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ test(input_reg, Operand(input_reg));
__ j(not_zero, &done);
__ movmskpd(input_reg, xmm0);
__ and_(input_reg, 1);
__ RecordComment("Deferred TaggedToI: minus zero");
DeoptimizeIf(not_zero, instr->environment());
}
__ TruncateHeapNumberToI(input_reg, input_reg);
} else {
UNREACHABLE();
Label bailout;
XMMRegister scratch = (instr->temp() != NULL)
? ToDoubleRegister(instr->temp())
: no_xmm_reg;
__ TaggedToI(input_reg, input_reg, scratch,
instr->hydrogen()->GetMinusZeroMode(), &bailout);
__ jmp(done);
__ bind(&bailout);
DeoptimizeIf(no_condition, instr->environment());
}
__ bind(&done);
}
......@@ -5405,7 +5351,7 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
const X87Stack& x87_stack)
: LDeferredCode(codegen, x87_stack), instr_(instr) { }
virtual void Generate() V8_OVERRIDE {
codegen()->DoDeferredTaggedToI(instr_);
codegen()->DoDeferredTaggedToI(instr_, done());
}
virtual LInstruction* instr() V8_OVERRIDE { return instr_; }
private:
......@@ -5426,171 +5372,6 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
}
void LCodeGen::DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr) {
Label done, heap_number;
Register result_reg = ToRegister(instr->result());
Register input_reg = ToRegister(instr->value());
// Heap number map check.
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
factory()->heap_number_map());
if (instr->truncating()) {
__ j(equal, &heap_number, Label::kNear);
// Check for undefined. Undefined is converted to zero for truncating
// conversions.
__ cmp(input_reg, factory()->undefined_value());
__ RecordComment("Deferred TaggedToI: cannot truncate");
DeoptimizeIf(not_equal, instr->environment());
__ xor_(result_reg, result_reg);
__ jmp(&done, Label::kFar);
__ bind(&heap_number);
} else {
// Deoptimize if we don't have a heap number.
DeoptimizeIf(not_equal, instr->environment());
}
// Surprisingly, all of this crazy bit manipulation is considerably
// faster than using the built-in x86 CPU conversion functions (about 6x).
Label right_exponent, adjust_bias, zero_result;
Register scratch = ToRegister(instr->scratch());
Register scratch2 = ToRegister(instr->scratch2());
// Get exponent word.
__ mov(scratch, FieldOperand(input_reg, HeapNumber::kExponentOffset));
// Get exponent alone in scratch2.
__ mov(scratch2, scratch);
__ and_(scratch2, HeapNumber::kExponentMask);
__ shr(scratch2, HeapNumber::kExponentShift);
if (instr->truncating()) {
__ j(zero, &zero_result);
} else {
__ j(not_zero, &adjust_bias);
__ test(scratch, Immediate(HeapNumber::kMantissaMask));
DeoptimizeIf(not_zero, instr->environment());
__ cmp(FieldOperand(input_reg, HeapNumber::kMantissaOffset), Immediate(0));
DeoptimizeIf(not_equal, instr->environment());
__ bind(&adjust_bias);
}
__ sub(scratch2, Immediate(HeapNumber::kExponentBias));
if (!instr->truncating()) {
DeoptimizeIf(negative, instr->environment());
} else {
__ j(negative, &zero_result);
}
// 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.
Register scratch3 = ToRegister(instr->scratch3());
__ mov(scratch3, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
__ xor_(result_reg, result_reg);
const uint32_t non_int32_exponent = 31;
__ cmp(scratch2, Immediate(non_int32_exponent));
// If we have a match of the int32 exponent then skip some logic.
__ j(equal, &right_exponent, Label::kNear);
// If the number doesn't find in an int32, deopt.
DeoptimizeIf(greater, instr->environment());
// Exponent word in scratch, exponent in scratch2. We know that 0 <= exponent
// < 31.
__ mov(result_reg, Immediate(31));
__ sub(result_reg, scratch2);
__ bind(&right_exponent);
// Save off exponent for negative check later.
__ mov(scratch2, scratch);
// Here result_reg 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 - 1;
__ shl(scratch, shift_distance);
if (!instr->truncating()) {
// If not truncating, a non-zero value in the bottom 22 bits means a
// non-integral value --> trigger a deopt.
__ test(scratch3, Immediate((1 << (32 - shift_distance)) - 1));
DeoptimizeIf(not_equal, instr->environment());
}
// Shift down 22 bits to get the most significant 10 bits or the low
// mantissa word.
__ shr(scratch3, 32 - shift_distance);
__ or_(scratch3, scratch);
if (!instr->truncating()) {
// If truncating, a non-zero value in the bits that will be shifted away
// when adjusting the exponent means rounding --> deopt.
__ mov(scratch, 0x1);
ASSERT(result_reg.is(ecx));
__ shl_cl(scratch);
__ dec(scratch);
__ test(scratch3, scratch);
DeoptimizeIf(not_equal, instr->environment());
}
// Move down according to the exponent.
ASSERT(result_reg.is(ecx));
__ shr_cl(scratch3);
// Now the unsigned 32-bit answer is in scratch3. We need to move it to
// result_reg and we may need to fix the sign.
Label negative_result;
__ xor_(result_reg, result_reg);
__ cmp(scratch2, result_reg);
__ j(less, &negative_result, Label::kNear);
__ cmp(scratch3, result_reg);
__ mov(result_reg, scratch3);
// If the result is > MAX_INT, result doesn't fit in signed 32-bit --> deopt.
DeoptimizeIf(less, instr->environment());
__ jmp(&done, Label::kNear);
__ bind(&zero_result);
__ xor_(result_reg, result_reg);
__ jmp(&done, Label::kNear);
__ bind(&negative_result);
__ sub(result_reg, scratch3);
if (!instr->truncating()) {
// -0.0 triggers a deopt.
DeoptimizeIf(zero, instr->environment());
}
// If the negative subtraction overflows into a positive number, there was an
// overflow --> deopt.
DeoptimizeIf(positive, instr->environment());
__ bind(&done);
}
void LCodeGen::DoTaggedToINoSSE2(LTaggedToINoSSE2* instr) {
class DeferredTaggedToINoSSE2 V8_FINAL : public LDeferredCode {
public:
DeferredTaggedToINoSSE2(LCodeGen* codegen,
LTaggedToINoSSE2* instr,
const X87Stack& x87_stack)
: LDeferredCode(codegen, x87_stack), instr_(instr) { }
virtual void Generate() V8_OVERRIDE {
codegen()->DoDeferredTaggedToINoSSE2(instr_);
}
virtual LInstruction* instr() V8_OVERRIDE { return instr_; }
private:
LTaggedToINoSSE2* instr_;
};
LOperand* input = instr->value();
ASSERT(input->IsRegister());
Register input_reg = ToRegister(input);
ASSERT(input_reg.is(ToRegister(instr->result())));
DeferredTaggedToINoSSE2* deferred =
new(zone()) DeferredTaggedToINoSSE2(this, instr, x87_stack_);
// Smi check.
__ JumpIfNotSmi(input_reg, deferred->entry());
__ SmiUntag(input_reg); // Untag smi.
__ bind(deferred->exit());
}
void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
LOperand* input = instr->value();
ASSERT(input->IsRegister());
......@@ -5637,73 +5418,33 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
ASSERT(result->IsRegister());
Register result_reg = ToRegister(result);
Label done;
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister input_reg = ToDoubleRegister(input);
__ 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.
Label fast_case_succeeded;
__ cmp(result_reg, 0x80000000u);
__ 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);
if (instr->truncating()) {
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister input_reg = ToDoubleRegister(input);
__ TruncateDoubleToI(result_reg, input_reg);
} else {
__ cvtsi2sd(xmm0, Operand(result_reg));
__ ucomisd(xmm0, input_reg);
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// The integer converted back is equal to the original. We
// only have to test if we got -0 as an input.
__ test(result_reg, Operand(result_reg));
__ j(not_zero, &done, Label::kNear);
__ movmskpd(result_reg, input_reg);
// Bit 0 contains the sign of the double in input_reg.
// If input was positive, we are ok and return 0, otherwise
// deoptimize.
__ and_(result_reg, 1);
DeoptimizeIf(not_zero, instr->environment());
}
__ bind(&done);
X87Register input_reg = ToX87Register(input);
X87Fxch(input_reg);
__ TruncateX87TOSToI(result_reg);
}
} else {
X87Register input_reg = ToX87Register(input);
__ push(result_reg);
X87Mov(Operand(esp, 0), input_reg, kX87IntOperand);
if (instr->truncating()) {
__ pop(result_reg);
Label bailout, done;
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister input_reg = ToDoubleRegister(input);
__ DoubleToI(result_reg, input_reg, xmm0,
instr->hydrogen()->GetMinusZeroMode(), &bailout, Label::kNear);
} else {
X87Register input_reg = ToX87Register(input);
X87Fxch(input_reg);
__ fld(0);
__ fild_s(Operand(esp, 0));
__ pop(result_reg);
__ FCmp();
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
}
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ test(result_reg, Operand(result_reg));
__ j(not_zero, &done, Label::kNear);
// To check for minus zero, we load the value again as float, and check
// if that is still 0.
X87Fxch(input_reg);
__ push(result_reg);
__ fst_s(Operand(esp, 0));
__ pop(result_reg);
__ test(result_reg, Operand(result_reg));
DeoptimizeIf(not_zero, instr->environment());
__ bind(&done);
__ X87TOSToI(result_reg, instr->hydrogen()->GetMinusZeroMode(),
&bailout, Label::kNear);
}
__ jmp(&done, Label::kNear);
__ bind(&bailout);
DeoptimizeIf(no_condition, instr->environment());
__ bind(&done);
}
}
......@@ -5715,55 +5456,23 @@ void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) {
ASSERT(result->IsRegister());
Register result_reg = ToRegister(result);
Label done;
Label bailout, done;
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
CpuFeatureScope scope(masm(), SSE2);
XMMRegister input_reg = ToDoubleRegister(input);
__ cvttsd2si(result_reg, Operand(input_reg));
__ cvtsi2sd(xmm0, Operand(result_reg));
__ ucomisd(xmm0, input_reg);
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// The integer converted back is equal to the original. We
// only have to test if we got -0 as an input.
__ test(result_reg, Operand(result_reg));
__ j(not_zero, &done, Label::kNear);
__ movmskpd(result_reg, input_reg);
// Bit 0 contains the sign of the double in input_reg.
// If input was positive, we are ok and return 0, otherwise
// deoptimize.
__ and_(result_reg, 1);
DeoptimizeIf(not_zero, instr->environment());
__ bind(&done);
}
__ DoubleToI(result_reg, input_reg, xmm0,
instr->hydrogen()->GetMinusZeroMode(), &bailout, Label::kNear);
} else {
X87Register input_reg = ToX87Register(input);
X87Fxch(input_reg);
__ push(result_reg);
X87Mov(Operand(esp, 0), input_reg, kX87IntOperand);
__ fld(0);
__ fild_s(Operand(esp, 0));
__ pop(result_reg);
__ FCmp();
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ test(result_reg, Operand(result_reg));
__ j(not_zero, &done, Label::kNear);
// To check for minus zero, we load the value again as float, and check
// if that is still 0.
__ push(result_reg);
__ fst_s(Operand(esp, 0));
__ pop(result_reg);
__ test(result_reg, Operand(result_reg));
DeoptimizeIf(not_zero, instr->environment());
__ bind(&done);
}
__ X87TOSToI(result_reg, instr->hydrogen()->GetMinusZeroMode(),
&bailout, Label::kNear);
}
__ jmp(&done, Label::kNear);
__ bind(&bailout);
DeoptimizeIf(no_condition, instr->environment());
__ bind(&done);
__ SmiTag(result_reg);
DeoptimizeIf(overflow, instr->environment());
}
......
......@@ -163,8 +163,7 @@ class LCodeGen V8_FINAL BASE_EMBEDDED {
LOperand* value,
IntegerSignedness signedness);
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr);
void DoDeferredTaggedToI(LTaggedToI* instr, Label* done);
void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
void DoDeferredStackCheck(LStackCheck* instr);
void DoDeferredRandom(LRandom* instr);
......@@ -549,6 +548,7 @@ class LDeferredCode : public ZoneObject {
void SetExit(Label* exit) { external_exit_ = exit; }
Label* entry() { return &entry_; }
Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; }
Label* done() { return &done_; }
int instruction_index() const { return instruction_index_; }
const LCodeGen::X87Stack& x87_stack() const { return x87_stack_; }
......@@ -561,6 +561,7 @@ class LDeferredCode : public ZoneObject {
Label entry_;
Label exit_;
Label* external_exit_;
Label done_;
int instruction_index_;
LCodeGen::X87Stack x87_stack_;
};
......
......@@ -1944,21 +1944,11 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
return DefineSameAsFirst(new(zone()) LSmiUntag(value, false));
} else {
bool truncating = instr->CanTruncateToInt32();
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
LOperand* value = UseRegister(val);
LOperand* xmm_temp =
(truncating && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
} else {
LOperand* value = UseFixed(val, ecx);
LTaggedToINoSSE2* res =
new(zone()) LTaggedToINoSSE2(value, TempRegister(),
TempRegister(), TempRegister());
return AssignEnvironment(DefineFixed(res, ecx));
}
LOperand* xmm_temp =
(CpuFeatures::IsSafeForSnapshot(SSE2) && !truncating)
? FixedTemp(xmm1) : NULL;
LTaggedToI* res = new(zone()) LTaggedToI(UseRegister(val), xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
}
}
} else if (from.IsDouble()) {
......@@ -1978,7 +1968,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
} else {
ASSERT(to.IsInteger32());
bool truncating = instr->CanTruncateToInt32();
bool needs_temp = truncating && !CpuFeatures::IsSupported(SSE3);
bool needs_temp = CpuFeatures::IsSafeForSnapshot(SSE2) && !truncating;
LOperand* value = needs_temp ?
UseTempRegister(instr->value()) : UseRegister(instr->value());
LOperand* temp = needs_temp ? TempRegister() : NULL;
......
......@@ -175,7 +175,6 @@ class LCodeGen;
V(StringCompareAndBranch) \
V(SubI) \
V(TaggedToI) \
V(TaggedToINoSSE2) \
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
......@@ -2171,31 +2170,6 @@ class LTaggedToI V8_FINAL : public LTemplateInstruction<1, 1, 1> {
};
// Truncating conversion from a tagged value to an int32.
class LTaggedToINoSSE2 V8_FINAL : public LTemplateInstruction<1, 1, 3> {
public:
LTaggedToINoSSE2(LOperand* value,
LOperand* temp1,
LOperand* temp2,
LOperand* temp3) {
inputs_[0] = value;
temps_[0] = temp1;
temps_[1] = temp2;
temps_[2] = temp3;
}
LOperand* value() { return inputs_[0]; }
LOperand* scratch() { return temps_[0]; }
LOperand* scratch2() { return temps_[1]; }
LOperand* scratch3() { return temps_[2]; }
DECLARE_CONCRETE_INSTRUCTION(TaggedToINoSSE2, "tagged-to-i-nosse2")
DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
};
class LSmiTag V8_FINAL : public LTemplateInstruction<1, 1, 0> {
public:
explicit LSmiTag(LOperand* value) {
......
......@@ -215,6 +215,231 @@ void MacroAssembler::ClampUint8(Register reg) {
}
void MacroAssembler::SlowTruncateToI(Register result_reg,
Register input_reg,
int offset) {
DoubleToIStub stub(input_reg, result_reg, offset, true);
call(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
}
void MacroAssembler::TruncateDoubleToI(Register result_reg,
XMMRegister input_reg) {
Label done;
cvttsd2si(result_reg, Operand(input_reg));
cmp(result_reg, 0x80000000u);
j(not_equal, &done, Label::kNear);
sub(esp, Immediate(kDoubleSize));
movdbl(MemOperand(esp, 0), input_reg);
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
bind(&done);
}
void MacroAssembler::TruncateX87TOSToI(Register result_reg) {
sub(esp, Immediate(kDoubleSize));
fst_d(MemOperand(esp, 0));
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
}
void MacroAssembler::X87TOSToI(Register result_reg,
MinusZeroMode minus_zero_mode,
Label* conversion_failed,
Label::Distance dst) {
Label done;
sub(esp, Immediate(kPointerSize));
fist_s(MemOperand(esp, 0));
fld(0);
fild_s(MemOperand(esp, 0));
pop(result_reg);
FCmp();
j(not_equal, conversion_failed, dst);
j(parity_even, conversion_failed, dst);
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(not_zero, &done, Label::kNear);
// To check for minus zero, we load the value again as float, and check
// if that is still 0.
sub(esp, Immediate(kPointerSize));
fst_s(MemOperand(esp, 0));
pop(result_reg);
test(result_reg, Operand(result_reg));
j(not_zero, conversion_failed, dst);
}
bind(&done);
}
void MacroAssembler::DoubleToI(Register result_reg,
XMMRegister input_reg,
XMMRegister scratch,
MinusZeroMode minus_zero_mode,
Label* conversion_failed,
Label::Distance dst) {
ASSERT(!input_reg.is(scratch));
Label done;
cvttsd2si(result_reg, Operand(input_reg));
cvtsi2sd(scratch, Operand(result_reg));
ucomisd(scratch, input_reg);
j(not_equal, conversion_failed, dst);
j(parity_even, conversion_failed, dst); // NaN.
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(not_zero, &done, Label::kNear);
movmskpd(result_reg, input_reg);
and_(result_reg, 1);
j(not_zero, conversion_failed, dst);
}
bind(&done);
}
void MacroAssembler::TruncateHeapNumberToI(Register result_reg,
Register input_reg) {
Label done, slow_case;
if (CpuFeatures::IsSupported(SSE3)) {
CpuFeatureScope scope(this, SSE3);
Label convert;
// Use more powerful conversion when sse3 is available.
// Load x87 register with heap number.
fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
// Get exponent alone and check for too-big exponent.
mov(result_reg, FieldOperand(input_reg, HeapNumber::kExponentOffset));
and_(result_reg, HeapNumber::kExponentMask);
const uint32_t kTooBigExponent =
(HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
cmp(Operand(result_reg), Immediate(kTooBigExponent));
j(greater_equal, &slow_case, Label::kNear);
// Reserve space for 64 bit answer.
sub(Operand(esp), Immediate(kDoubleSize));
// Do conversion, which cannot fail because we checked the exponent.
fisttp_d(Operand(esp, 0));
mov(result_reg, Operand(esp, 0)); // Low word of answer is the result.
add(Operand(esp), Immediate(kDoubleSize));
jmp(&done, Label::kNear);
// Slow case.
bind(&slow_case);
if (input_reg.is(result_reg)) {
// Input is clobbered. Restore number from fpu stack
sub(Operand(esp), Immediate(kDoubleSize));
fstp_d(Operand(esp, 0));
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
} else {
fstp(0);
SlowTruncateToI(result_reg, input_reg);
}
} else if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatureScope scope(this, SSE2);
movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
cvttsd2si(result_reg, Operand(xmm0));
cmp(result_reg, 0x80000000u);
j(not_equal, &done, Label::kNear);
// Check if the input was 0x8000000 (kMinInt).
// If no, then we got an overflow and we deoptimize.
ExternalReference min_int = ExternalReference::address_of_min_int();
ucomisd(xmm0, Operand::StaticVariable(min_int));
j(not_equal, &slow_case, Label::kNear);
j(parity_even, &slow_case, Label::kNear); // NaN.
jmp(&done, Label::kNear);
// Slow case.
bind(&slow_case);
if (input_reg.is(result_reg)) {
// Input is clobbered. Restore number from double scratch.
sub(esp, Immediate(kDoubleSize));
movdbl(MemOperand(esp, 0), xmm0);
SlowTruncateToI(result_reg, esp, 0);
add(esp, Immediate(kDoubleSize));
} else {
SlowTruncateToI(result_reg, input_reg);
}
} else {
SlowTruncateToI(result_reg, input_reg);
}
bind(&done);
}
void MacroAssembler::TaggedToI(Register result_reg,
Register input_reg,
XMMRegister temp,
MinusZeroMode minus_zero_mode,
Label* lost_precision) {
Label done;
ASSERT(!temp.is(xmm0));
cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
isolate()->factory()->heap_number_map());
j(not_equal, lost_precision, Label::kNear);
if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
ASSERT(!temp.is(no_xmm_reg));
CpuFeatureScope scope(this, SSE2);
movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
cvttsd2si(result_reg, Operand(xmm0));
cvtsi2sd(temp, Operand(result_reg));
ucomisd(xmm0, temp);
RecordComment("Deferred TaggedToI: lost precision");
j(not_equal, lost_precision, Label::kNear);
RecordComment("Deferred TaggedToI: NaN");
j(parity_even, lost_precision, Label::kNear);
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(not_zero, &done, Label::kNear);
movmskpd(result_reg, xmm0);
and_(result_reg, 1);
RecordComment("Deferred TaggedToI: minus zero");
j(not_zero, lost_precision, Label::kNear);
}
} else {
// TODO(olivf) Converting a number on the fpu is actually quite slow. We
// should first try a fast conversion and then bailout to this slow case.
Label lost_precision_pop, zero_check;
Label* lost_precision_int = (minus_zero_mode == FAIL_ON_MINUS_ZERO)
? &lost_precision_pop : lost_precision;
sub(esp, Immediate(kPointerSize));
fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) fld(0);
fist_s(MemOperand(esp, 0));
fild_s(MemOperand(esp, 0));
FCmp();
pop(result_reg);
j(not_equal, lost_precision_int, Label::kNear);
j(parity_even, lost_precision_int, Label::kNear); // NaN.
if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
test(result_reg, Operand(result_reg));
j(zero, &zero_check, Label::kNear);
fstp(0);
jmp(&done, Label::kNear);
bind(&zero_check);
// To check for minus zero, we load the value again as float, and check
// if that is still 0.
sub(esp, Immediate(kPointerSize));
fstp_s(Operand(esp, 0));
pop(result_reg);
test(result_reg, Operand(result_reg));
j(zero, &done, Label::kNear);
jmp(lost_precision, Label::kNear);
bind(&lost_precision_pop);
fstp(0);
jmp(lost_precision, Label::kNear);
}
}
bind(&done);
}
static double kUint32Bias =
static_cast<double>(static_cast<uint32_t>(0xFFFFFFFF)) + 1;
......
......@@ -474,6 +474,21 @@ class MacroAssembler: public Assembler {
XMMRegister scratch_reg,
Register result_reg);
void SlowTruncateToI(Register result_reg, Register input_reg,
int offset = HeapNumber::kValueOffset - kHeapObjectTag);
void TruncateHeapNumberToI(Register result_reg, Register input_reg);
void TruncateDoubleToI(Register result_reg, XMMRegister input_reg);
void TruncateX87TOSToI(Register result_reg);
void DoubleToI(Register result_reg, XMMRegister input_reg,
XMMRegister scratch, MinusZeroMode minus_zero_mode,
Label* conversion_failed, Label::Distance dst = Label::kFar);
void X87TOSToI(Register result_reg, MinusZeroMode minus_zero_mode,
Label* conversion_failed, Label::Distance dst = Label::kFar);
void TaggedToI(Register result_reg, Register input_reg, XMMRegister temp,
MinusZeroMode minus_zero_mode, Label* lost_precision);
// Smi tagging support.
void SmiTag(Register reg) {
......
......@@ -565,6 +565,11 @@ enum ClearExceptionFlag {
};
enum MinusZeroMode {
TREAT_MINUS_ZERO_AS_ZERO,
FAIL_ON_MINUS_ZERO
};
} } // namespace v8::internal
#endif // V8_V8GLOBALS_H_
......@@ -679,3 +679,37 @@ boo(built_in_array, 0, 2.5);
assertEquals(2.5, goo(built_in_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
// Check all int range edge cases
function checkRange() {
var e32 = Math.pow(2,32); var e31 = Math.pow(2,31);
var e16 = Math.pow(2,16); var e15 = Math.pow(2,15);
var e8 = Math.pow(2,8); var e7 = Math.pow(2,7);
var a7 = new Uint32Array(2); var a71 = new Int32Array(2);
var a72 = new Uint16Array(2); var a73 = new Int16Array(2);
var a74 = new Uint8Array(2); var a75 = new Int8Array(2);
for (i = 1; i <= Math.pow(2,33); i *= 2) {
var j = i-1;
a7[0] = i; a71[0] = i; a72[0] = i; a73[0] = i; a74[0] = i; a75[0] = i;
a7[1] = j; a71[1] = j; a72[1] = j; a73[1] = j; a74[1] = j; a75[1] = j;
if (i < e32) { assertEquals(a7[0], i); } else { assertEquals(a7[0], 0); }
if (j < e32) { assertEquals(a7[1], j); } else { assertEquals(a7[1],e32-1); }
if (i < e31) { assertEquals(a71[0], i); } else {
assertEquals(a71[0], (i < e32) ? -e31 : 0 ); }
if (j < e31) { assertEquals(a71[1], j); } else { assertEquals(a71[1], -1); }
if (i < e16) { assertEquals(a72[0], i); } else { assertEquals(a72[0], 0); }
if (j < e16) { assertEquals(a72[1], j); } else { assertEquals(a72[1], e16-1); }
if (i < e15) { assertEquals(a73[0], i); } else {
assertEquals(a73[0], (i < e16) ? -e15 : 0 ); }
if (j < e15) { assertEquals(a73[1], j); } else { assertEquals(a73[1], -1); }
if (i < e8) { assertEquals(a74[0], i); } else { assertEquals(a74[0], 0); }
if (j < e8) { assertEquals(a74[1], j); } else { assertEquals(a74[1], e8-1); }
if (i < e7) { assertEquals(a75[0], i); } else {
assertEquals(a75[0], (i < e8) ? -e7 : 0); }
if (j < e7) { assertEquals(a75[1], j); } else { assertEquals(a75[1], -1); }
}
}
checkRange();
......@@ -678,3 +678,37 @@ boo(built_in_array, 0, 2.5);
assertEquals(2.5, goo(built_in_array, 0));
%ClearFunctionTypeFeedback(goo);
%ClearFunctionTypeFeedback(boo);
// Check all int range edge cases
function checkRange() {
var e32 = Math.pow(2,32); var e31 = Math.pow(2,31);
var e16 = Math.pow(2,16); var e15 = Math.pow(2,15);
var e8 = Math.pow(2,8); var e7 = Math.pow(2,7);
var a7 = new Uint32Array(2); var a71 = new Int32Array(2);
var a72 = new Uint16Array(2); var a73 = new Int16Array(2);
var a74 = new Uint8Array(2); var a75 = new Int8Array(2);
for (i = 1; i <= Math.pow(2,33); i *= 2) {
var j = i-1;
a7[0] = i; a71[0] = i; a72[0] = i; a73[0] = i; a74[0] = i; a75[0] = i;
a7[1] = j; a71[1] = j; a72[1] = j; a73[1] = j; a74[1] = j; a75[1] = j;
if (i < e32) { assertEquals(a7[0], i); } else { assertEquals(a7[0], 0); }
if (j < e32) { assertEquals(a7[1], j); } else { assertEquals(a7[1],e32-1); }
if (i < e31) { assertEquals(a71[0], i); } else {
assertEquals(a71[0], (i < e32) ? -e31 : 0 ); }
if (j < e31) { assertEquals(a71[1], j); } else { assertEquals(a71[1], -1); }
if (i < e16) { assertEquals(a72[0], i); } else { assertEquals(a72[0], 0); }
if (j < e16) { assertEquals(a72[1], j); } else { assertEquals(a72[1], e16-1); }
if (i < e15) { assertEquals(a73[0], i); } else {
assertEquals(a73[0], (i < e16) ? -e15 : 0 ); }
if (j < e15) { assertEquals(a73[1], j); } else { assertEquals(a73[1], -1); }
if (i < e8) { assertEquals(a74[0], i); } else { assertEquals(a74[0], 0); }
if (j < e8) { assertEquals(a74[1], j); } else { assertEquals(a74[1], e8-1); }
if (i < e7) { assertEquals(a75[0], i); } else {
assertEquals(a75[0], (i < e8) ? -e7 : 0); }
if (j < e7) { assertEquals(a75[1], j); } else { assertEquals(a75[1], -1); }
}
}
checkRange();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment