From: olivf@chromium.org Date: Mon, 2 Sep 2013 09:30:54 +0000 (+0000) Subject: Move ToI conversions to the MacroAssembler X-Git-Tag: upstream/4.7.83~12718 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=78df13d0d5273f1df8b1bfc4c35bf0faeeac474a;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index dd42190..0357805 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -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. diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index d6b6dab..551d031 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -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(double_register_5)) #define xmm6 (static_cast(double_register_6)) #define xmm7 (static_cast(double_register_7)) +#define no_xmm_reg (static_cast(no_double_reg)) struct X87Register : IntelDoubleRegister { diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 42b1296..7891dc6 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -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 diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index b2c66b3..75525ed 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -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()); } diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index 37a7ea9..23b2e48 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -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_; }; diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index e6cc719..8d66085 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -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; diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index e795926..8b311b7 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -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) { diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index aaacbd9..bd058f8 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -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(static_cast(0xFFFFFFFF)) + 1; diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 4d9ebad..e4e4533 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -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) { diff --git a/src/v8globals.h b/src/v8globals.h index 745fccb..7fa2fd6 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -565,6 +565,11 @@ enum ClearExceptionFlag { }; +enum MinusZeroMode { + TREAT_MINUS_ZERO_AS_ZERO, + FAIL_ON_MINUS_ZERO +}; + } } // namespace v8::internal #endif // V8_V8GLOBALS_H_ diff --git a/test/mjsunit/external-array-no-sse2.js b/test/mjsunit/external-array-no-sse2.js index 11e61ba..575a8b5 100644 --- a/test/mjsunit/external-array-no-sse2.js +++ b/test/mjsunit/external-array-no-sse2.js @@ -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(); diff --git a/test/mjsunit/external-array.js b/test/mjsunit/external-array.js index 3fcd544..ab5435e 100644 --- a/test/mjsunit/external-array.js +++ b/test/mjsunit/external-array.js @@ -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();