Chromium Code Reviews| Index: src/ia32/codegen-ia32.cc |
| =================================================================== |
| --- src/ia32/codegen-ia32.cc (revision 4214) |
| +++ src/ia32/codegen-ia32.cc (working copy) |
| @@ -911,6 +911,7 @@ |
| // operand in register number. Returns operand as floating point number |
| // on FPU stack. |
| static void LoadFloatOperand(MacroAssembler* masm, Register number); |
| + |
| // Code pattern for loading floating point values. Input values must |
| // be either smi or heap number objects (fp values). Requirements: |
| // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax. |
| @@ -929,6 +930,7 @@ |
| static void CheckFloatOperands(MacroAssembler* masm, |
| Label* non_float, |
| Register scratch); |
| + |
| // Takes the operands in edx and eax and loads them as integers in eax |
| // and ecx. |
| static void LoadAsIntegers(MacroAssembler* masm, |
| @@ -947,6 +949,7 @@ |
| // into xmm0 and xmm1 if they are. Operands are in edx and eax. |
| // Leaves operands unchanged. |
| static void LoadSSE2Operands(MacroAssembler* masm); |
| + |
| // Test if operands are numbers (smi or HeapNumber objects), and load |
| // them into xmm0 and xmm1 if they are. Jump to label not_numbers if |
| // either operand is not a number. Operands are in edx and eax. |
| @@ -2361,6 +2364,22 @@ |
| } |
| +// Convert from signed to unsigned comparison to match the way EFLAGS are set |
| +// by FPU and XMM compare instructions. |
| +static Condition DoubleCondition(Condition cc) { |
| + switch (cc) { |
| + case less: return below; |
| + case equal: return equal; |
| + case less_equal: return below_equal; |
| + case greater: return above; |
| + case greater_equal: return above_equal; |
| + default: UNREACHABLE(); |
| + } |
| + UNREACHABLE(); |
| + return equal; |
| +} |
| + |
| + |
| void CodeGenerator::Comparison(AstNode* node, |
| Condition cc, |
| bool strict, |
| @@ -2431,7 +2450,7 @@ |
| left_side = right_side; |
| right_side = temp; |
| cc = ReverseCondition(cc); |
| - // This may reintroduce greater or less_equal as the value of cc. |
| + // This may re-introduce greater or less_equal as the value of cc. |
| // CompareStub and the inline code both support all values of cc. |
| } |
| // Implement comparison against a constant Smi, inlining the case |
| @@ -2480,16 +2499,7 @@ |
| // Jump to builtin for NaN. |
| not_number.Branch(parity_even, &left_side); |
| left_side.Unuse(); |
| - Condition double_cc = cc; |
| - switch (cc) { |
| - case less: double_cc = below; break; |
| - case equal: double_cc = equal; break; |
| - case less_equal: double_cc = below_equal; break; |
| - case greater: double_cc = above; break; |
| - case greater_equal: double_cc = above_equal; break; |
| - default: UNREACHABLE(); |
| - } |
| - dest->true_target()->Branch(double_cc); |
| + dest->true_target()->Branch(DoubleCondition(cc)); |
| dest->false_target()->Jump(); |
| not_number.Bind(&left_side); |
| } |
| @@ -2688,21 +2698,54 @@ |
| dest->Split(cc); |
| } |
| } else { |
| - // Neither side is a constant Smi or null. |
| - // If either side is a non-smi constant, skip the smi check. |
| + // Neither side is a constant Smi, constant 1-char string or constant null. |
| + // If either side is a non-smi constant, or known to be a heap number skip |
| + // the smi check. |
| bool known_non_smi = |
| (left_side.is_constant() && !left_side.handle()->IsSmi()) || |
| - (right_side.is_constant() && !right_side.handle()->IsSmi()); |
| + (right_side.is_constant() && !right_side.handle()->IsSmi()) || |
| + left_side.number_info().IsHeapNumber() || |
| + right_side.number_info().IsHeapNumber(); |
| NaNInformation nan_info = |
| (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ? |
| kBothCouldBeNaN : |
| kCantBothBeNaN; |
| + |
| + // Inline number comparison handling any combination of smi's and heap |
| + // numbers if: |
| + // code is in a loop |
| + // the compare operation is different from equal |
| + // compare is not a for-loop comparison |
| + // The reason for excluding equal is that it will most likely be done |
| + // with smi's (not heap numbers) and the code to comparing smi's is inlined |
| + // separately. The same reason applies for for-loop comparison which will |
| + // also most likely be smi comparisons. |
| + bool is_for_loop_compare = (node->AsCompareOperation() != NULL) |
| + && node->AsCompareOperation()->is_for_loop_condition(); |
| + bool inline_number_compare = loop_nesting() > 0 |
| + && cc != equal |
| + && !is_for_loop_compare; |
| + |
| + // Left and right needed in registers for the following code. |
| left_side.ToRegister(); |
| right_side.ToRegister(); |
| if (known_non_smi) { |
| - // When non-smi, call out to the compare stub. |
| - CompareStub stub(cc, strict, nan_info); |
| + // Inline the equality check if both operands can't be a NaN. If both |
| + // objects are the same they are equal. |
| + if (nan_info == kCantBothBeNaN && cc == equal) { |
|
Erik Corry
2010/03/23 18:09:24
This seems wrong. If one side is a constant fp nu
Søren Thygesen Gjesse
2010/03/23 20:40:01
This will only provide true when the objects are t
|
| + __ cmp(left_side.reg(), Operand(right_side.reg())); |
| + dest->true_target()->Branch(equal); |
| + } |
| + |
| + // Inline number comparison. |
| + if (inline_number_compare) { |
| + GenerateInlineNumberComparison(&left_side, &right_side, cc, dest); |
| + } |
| + |
| + // End of in-line compare, call out to the compare stub. Don't include |
| + // number comparison in the stub if it was inlined. |
| + CompareStub stub(cc, strict, nan_info, !inline_number_compare); |
| Result answer = frame_->CallStub(&stub, &left_side, &right_side); |
| if (cc == equal) { |
| __ test(answer.reg(), Operand(answer.reg())); |
| @@ -2721,6 +2764,7 @@ |
| Register left_reg = left_side.reg(); |
| Register right_reg = right_side.reg(); |
| + // In-line check for comparing two smis. |
| Result temp = allocator_->Allocate(); |
| ASSERT(temp.is_valid()); |
| __ mov(temp.reg(), left_side.reg()); |
| @@ -2728,8 +2772,22 @@ |
| __ test(temp.reg(), Immediate(kSmiTagMask)); |
| temp.Unuse(); |
| is_smi.Branch(zero, taken); |
| - // When non-smi, call out to the compare stub. |
| - CompareStub stub(cc, strict, nan_info); |
| + |
| + // Inline the equality check if both operands can't be a NaN. If both |
| + // objects are the same they are equal. |
| + if (nan_info == kCantBothBeNaN && cc == equal) { |
| + __ cmp(left_side.reg(), Operand(right_side.reg())); |
| + dest->true_target()->Branch(equal); |
| + } |
| + |
| + // Inline number comparison. |
| + if (inline_number_compare) { |
| + GenerateInlineNumberComparison(&left_side, &right_side, cc, dest); |
| + } |
| + |
| + // End of in-line compare, call out to the compare stub. Don't include |
| + // number comparison in the stub if it was inlined. |
| + CompareStub stub(cc, strict, nan_info, !inline_number_compare); |
| Result answer = frame_->CallStub(&stub, &left_side, &right_side); |
| if (cc == equal) { |
| __ test(answer.reg(), Operand(answer.reg())); |
| @@ -2752,6 +2810,150 @@ |
| } |
| +// Check that the comparison operand is a number. Jump to not_numbers jump |
| +// target passing the left and right result if the operand is not a number. |
| +static void CheckComparisonOperand(MacroAssembler* masm_, |
| + Result* operand, |
| + Result* left_side, |
| + Result* right_side, |
| + JumpTarget* not_numbers) { |
| + // Perform check if operand is not known to be a number. |
| + if (!operand->number_info().IsNumber()) { |
| + Label done; |
| + __ test(operand->reg(), Immediate(kSmiTagMask)); |
| + __ j(zero, &done); |
| + __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset), |
| + Immediate(Factory::heap_number_map())); |
| + not_numbers->Branch(not_equal, left_side, right_side, not_taken); |
| + __ bind(&done); |
| + } |
| +} |
| + |
| + |
| +// Load a comparison operand to the FPU stack. This assumes that the operand has |
| +// already been checked and is a number. |
| +static void LoadComparisonOperand(MacroAssembler* masm_, |
| + Result* operand, |
| + Result* left_side, |
| + Result* right_side) { |
| + Label done; |
| + if (operand->number_info().IsHeapNumber()) { |
| + // Operand is known to be a heap number, just load it. |
| + __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset)); |
| + } else if (operand->number_info().IsSmi()) { |
| + // Operand is known to be a smi. Convert it to double and keep the original |
| + // smi. |
| + __ SmiUntag(operand->reg()); |
| + __ push(operand->reg()); |
| + __ fild_s(Operand(esp, 0)); |
| + __ pop(operand->reg()); |
| + __ SmiTag(operand->reg()); |
| + } else { |
| + // Operand type not known, check for smi otherwise assume heap number. |
| + Label smi; |
| + __ test(operand->reg(), Immediate(kSmiTagMask)); |
| + __ j(zero, &smi); |
| + __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset)); |
| + __ jmp(&done); |
| + __ bind(&smi); |
| + __ SmiUntag(operand->reg()); |
| + __ push(operand->reg()); |
| + __ fild_s(Operand(esp, 0)); |
| + __ pop(operand->reg()); |
| + __ SmiTag(operand->reg()); |
| + __ jmp(&done); |
| + } |
| + __ bind(&done); |
| +} |
| + |
| + |
| +// Load a comparison operand into into a XMM register. Jump to not_numbers jump |
| +// target passing the left and right result if the operand is not a number. |
| +static void LoadComparisonOperandSSE2(MacroAssembler* masm_, |
| + Result* operand, |
| + XMMRegister reg, |
| + Result* left_side, |
| + Result* right_side, |
| + JumpTarget* not_numbers) { |
| + Label done; |
| + if (operand->number_info().IsHeapNumber()) { |
| + // Operand is known to be a heap number, just load it. |
| + __ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset)); |
| + } else if (operand->number_info().IsSmi()) { |
| + // Operand is known to be a smi. Convert it to double and keep the original |
| + // smi. |
| + __ SmiUntag(operand->reg()); |
| + __ cvtsi2sd(reg, Operand(operand->reg())); |
| + __ SmiTag(left_side->reg()); |
| + } else { |
| + // Operand type not known, check for smi or heap number. |
| + Label smi; |
| + __ test(operand->reg(), Immediate(kSmiTagMask)); |
| + __ j(zero, &smi); |
| + if (!operand->number_info().IsNumber()) { |
| + __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset), |
| + Immediate(Factory::heap_number_map())); |
| + not_numbers->Branch(not_equal, left_side, right_side, taken); |
| + } |
| + __ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset)); |
| + __ jmp(&done); |
| + |
| + __ bind(&smi); |
| + // Comvert smi to float and keep the original smi. |
| + __ SmiUntag(operand->reg()); |
| + __ cvtsi2sd(reg, Operand(operand->reg())); |
| + __ SmiTag(operand->reg()); |
| + __ jmp(&done); |
| + } |
| + __ bind(&done); |
| +} |
| + |
| + |
| +void CodeGenerator::GenerateInlineNumberComparison(Result* left_side, |
| + Result* right_side, |
| + Condition cc, |
| + ControlDestination* dest) { |
| + ASSERT(left_side->is_register()); |
| + ASSERT(right_side->is_register()); |
| + |
| + JumpTarget not_numbers; |
| + if (CpuFeatures::IsSupported(SSE2)) { |
| + CpuFeatures::Scope use_sse2(SSE2); |
| + |
| + // Load left and right operand into registers xmm0 and xmm1 and compare. |
| + LoadComparisonOperandSSE2(masm_, left_side, xmm0, left_side, right_side, |
| + ¬_numbers); |
| + LoadComparisonOperandSSE2(masm_, right_side, xmm1, left_side, right_side, |
| + ¬_numbers); |
| + __ comisd(xmm0, xmm1); |
| + } else { |
| + Label check_right, compare; |
| + |
| + // Make sure that both comparison operands are numbers. |
| + CheckComparisonOperand(masm_, left_side, left_side, right_side, |
| + ¬_numbers); |
| + CheckComparisonOperand(masm_, right_side, left_side, right_side, |
| + ¬_numbers); |
| + |
| + // Load right and left operand to FPU stack and compare. |
| + LoadComparisonOperand(masm_, right_side, left_side, right_side); |
| + LoadComparisonOperand(masm_, left_side, left_side, right_side); |
| + __ FCmp(); |
| + } |
| + |
| + // Bail out if a NaN is involved. |
| + not_numbers.Branch(parity_even, left_side, right_side, not_taken); |
| + |
| + // Split to destination targets based on comparison. |
| + left_side->Unuse(); |
| + right_side->Unuse(); |
| + dest->true_target()->Branch(DoubleCondition(cc)); |
| + dest->false_target()->Jump(); |
| + |
| + not_numbers.Bind(left_side, right_side); |
| +} |
| + |
| + |
| // Call the function just below TOS on the stack with the given |
| // arguments. The receiver is the TOS. |
| void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, |
| @@ -10821,63 +11023,70 @@ |
| __ push(edx); |
| __ push(ecx); |
| - // Inlined floating point compare. |
| - // Call builtin if operands are not floating point or smi. |
| - Label check_for_symbols; |
| - Label unordered; |
| - if (CpuFeatures::IsSupported(SSE2)) { |
| - CpuFeatures::Scope use_sse2(SSE2); |
| - CpuFeatures::Scope use_cmov(CMOV); |
| + // Generate the number comparison code. |
| + if (include_number_compare_) { |
| + Label non_number_comparison; |
| + Label unordered; |
| + if (CpuFeatures::IsSupported(SSE2)) { |
| + CpuFeatures::Scope use_sse2(SSE2); |
| + CpuFeatures::Scope use_cmov(CMOV); |
| - FloatingPointHelper::LoadSSE2Operands(masm, &check_for_symbols); |
| - __ comisd(xmm0, xmm1); |
| + FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison); |
| + __ comisd(xmm0, xmm1); |
| - // Jump to builtin for NaN. |
| - __ j(parity_even, &unordered, not_taken); |
| - __ mov(eax, 0); // equal |
| - __ mov(ecx, Immediate(Smi::FromInt(1))); |
| - __ cmov(above, eax, Operand(ecx)); |
| - __ mov(ecx, Immediate(Smi::FromInt(-1))); |
| - __ cmov(below, eax, Operand(ecx)); |
| - __ ret(2 * kPointerSize); |
| - } else { |
| - FloatingPointHelper::CheckFloatOperands(masm, &check_for_symbols, ebx); |
| - FloatingPointHelper::LoadFloatOperands(masm, ecx); |
| - __ FCmp(); |
| + // Don't base result on EFLAGS when a NaN is involved. |
| + __ j(parity_even, &unordered, not_taken); |
| + // Return a result of -1, 0, or 1, based on EFLAGS. |
| + __ mov(eax, 0); // equal |
| + __ mov(ecx, Immediate(Smi::FromInt(1))); |
| + __ cmov(above, eax, Operand(ecx)); |
| + __ mov(ecx, Immediate(Smi::FromInt(-1))); |
| + __ cmov(below, eax, Operand(ecx)); |
| + __ ret(2 * kPointerSize); |
| + } else { |
| + FloatingPointHelper::CheckFloatOperands( |
| + masm, &non_number_comparison, ebx); |
| + FloatingPointHelper::LoadFloatOperands(masm, ecx); |
| + __ FCmp(); |
| - // Jump to builtin for NaN. |
| - __ j(parity_even, &unordered, not_taken); |
| + // Don't base result on EFLAGS when a NaN is involved. |
| + __ j(parity_even, &unordered, not_taken); |
| - Label below_lbl, above_lbl; |
| - // Return a result of -1, 0, or 1, to indicate result of comparison. |
| - __ j(below, &below_lbl, not_taken); |
| - __ j(above, &above_lbl, not_taken); |
| + Label below_label, above_label; |
| + // Return a result of -1, 0, or 1, based on EFLAGS. In all cases remove |
| + // two arguments from the stack as they have been pushed in preparation |
| + // of a possible runtime call. |
| + __ j(below, &below_label, not_taken); |
| + __ j(above, &above_label, not_taken); |
| - __ xor_(eax, Operand(eax)); // equal |
| - // Both arguments were pushed in case a runtime call was needed. |
| - __ ret(2 * kPointerSize); |
| + __ xor_(eax, Operand(eax)); |
| + __ ret(2 * kPointerSize); |
| - __ bind(&below_lbl); |
| - __ mov(eax, Immediate(Smi::FromInt(-1))); |
| - __ ret(2 * kPointerSize); |
| + __ bind(&below_label); |
| + __ mov(eax, Immediate(Smi::FromInt(-1))); |
| + __ ret(2 * kPointerSize); |
| - __ bind(&above_lbl); |
| - __ mov(eax, Immediate(Smi::FromInt(1))); |
| + __ bind(&above_label); |
| + __ mov(eax, Immediate(Smi::FromInt(1))); |
| + __ ret(2 * kPointerSize); |
| + } |
| + |
| + // If one of the numbers was NaN, then the result is always false. |
| + // The cc is never not-equal. |
| + __ bind(&unordered); |
| + ASSERT(cc_ != not_equal); |
| + if (cc_ == less || cc_ == less_equal) { |
| + __ mov(eax, Immediate(Smi::FromInt(1))); |
| + } else { |
| + __ mov(eax, Immediate(Smi::FromInt(-1))); |
| + } |
| __ ret(2 * kPointerSize); // eax, edx were pushed |
| + |
| + // The number comparison code did not provide a valid result. |
| + __ bind(&non_number_comparison); |
| } |
| - // If one of the numbers was NaN, then the result is always false. |
| - // The cc is never not-equal. |
| - __ bind(&unordered); |
| - ASSERT(cc_ != not_equal); |
| - if (cc_ == less || cc_ == less_equal) { |
| - __ mov(eax, Immediate(Smi::FromInt(1))); |
| - } else { |
| - __ mov(eax, Immediate(Smi::FromInt(-1))); |
| - } |
| - __ ret(2 * kPointerSize); // eax, edx were pushed |
| // Fast negative check for symbol-to-symbol equality. |
| - __ bind(&check_for_symbols); |
| Label check_for_strings; |
| if (cc_ == equal) { |
| BranchIfNonSymbol(masm, &check_for_strings, eax, ecx); |
| @@ -11534,10 +11743,11 @@ |
| // Encode the three parameters in a unique 16 bit value. To avoid duplicate |
| // stubs the never NaN NaN condition is only taken into account if the |
| // condition is equals. |
| - ASSERT(static_cast<unsigned>(cc_) < (1 << 14)); |
| + ASSERT(static_cast<unsigned>(cc_) < (1 << 13)); |
| return ConditionField::encode(static_cast<unsigned>(cc_)) |
| | StrictField::encode(strict_) |
| - | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false); |
| + | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false) |
| + | IncludeNumberCompareField::encode(include_number_compare_); |
| } |
| @@ -12171,6 +12381,9 @@ |
| Label result_not_equal; |
| Label result_greater; |
| Label compare_lengths; |
| + |
| + __ IncrementCounter(&Counters::string_compare_native, 1); |
| + |
| // Find minimum length. |
| Label left_shorter; |
| __ mov(scratch1, FieldOperand(left, String::kLengthOffset)); |
| @@ -12268,7 +12481,6 @@ |
| __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime); |
| // Compare flat ascii strings. |
| - __ IncrementCounter(&Counters::string_compare_native, 1); |
| GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi); |
| // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) |