Index: src/ia32/codegen-ia32.cc |
=================================================================== |
--- src/ia32/codegen-ia32.cc (revision 4259) |
+++ 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,53 @@ |
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().IsDouble() || |
+ right_side.number_info().IsDouble(); |
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_loop_condition = (node->AsExpression() != NULL) |
+ && node->AsExpression()->is_loop_condition(); |
+ bool inline_number_compare = |
+ loop_nesting() > 0 && cc != equal && !is_loop_condition; |
+ |
+ // 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) { |
+ __ 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 +2763,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 +2771,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 +2809,148 @@ |
} |
+// 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) { |
+ Label done; |
+ if (operand->number_info().IsDouble()) { |
+ // 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().IsDouble()) { |
+ // 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(operand->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); |
+ LoadComparisonOperand(masm_, left_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, |
@@ -10877,63 +11076,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); |
@@ -11543,57 +11749,59 @@ |
} |
+int CompareStub::MinorKey() { |
+ // 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 << 13)); |
+ return ConditionField::encode(static_cast<unsigned>(cc_)) |
+ | StrictField::encode(strict_) |
+ | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false) |
+ | IncludeNumberCompareField::encode(include_number_compare_); |
+} |
+ |
+ |
// Unfortunately you have to run without snapshots to see most of these |
// names in the profile since most compare stubs end up in the snapshot. |
const char* CompareStub::GetName() { |
+ if (name_ != NULL) return name_; |
+ const int kMaxNameLength = 100; |
+ name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength); |
+ if (name_ == NULL) return "OOM"; |
+ |
+ const char* cc_name; |
switch (cc_) { |
- case less: return "CompareStub_LT"; |
- case greater: return "CompareStub_GT"; |
- case less_equal: return "CompareStub_LE"; |
- case greater_equal: return "CompareStub_GE"; |
- case not_equal: { |
- if (strict_) { |
- if (never_nan_nan_) { |
- return "CompareStub_NE_STRICT_NO_NAN"; |
- } else { |
- return "CompareStub_NE_STRICT"; |
- } |
- } else { |
- if (never_nan_nan_) { |
- return "CompareStub_NE_NO_NAN"; |
- } else { |
- return "CompareStub_NE"; |
- } |
- } |
- } |
- case equal: { |
- if (strict_) { |
- if (never_nan_nan_) { |
- return "CompareStub_EQ_STRICT_NO_NAN"; |
- } else { |
- return "CompareStub_EQ_STRICT"; |
- } |
- } else { |
- if (never_nan_nan_) { |
- return "CompareStub_EQ_NO_NAN"; |
- } else { |
- return "CompareStub_EQ"; |
- } |
- } |
- } |
- default: return "CompareStub"; |
+ case less: cc_name = "LT"; break; |
+ case greater: cc_name = "GT"; break; |
+ case less_equal: cc_name = "LE"; break; |
+ case greater_equal: cc_name = "GE"; break; |
+ case equal: cc_name = "EQ"; break; |
+ case not_equal: cc_name = "NE"; break; |
+ default: cc_name = "UnknownCondition"; break; |
} |
-} |
+ const char* strict_name = ""; |
+ if (strict_ && (cc_ == equal || cc_ == not_equal)) { |
+ strict_name = "_STRICT"; |
+ } |
-int CompareStub::MinorKey() { |
- // 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)); |
- return ConditionField::encode(static_cast<unsigned>(cc_)) |
- | StrictField::encode(strict_) |
- | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false); |
+ const char* never_nan_nan_name = ""; |
+ if (never_nan_nan_ && (cc_ == equal || cc_ == not_equal)) { |
+ never_nan_nan_name = "_NO_NAN"; |
+ } |
+ |
+ const char* include_number_compare_name = ""; |
+ if (!include_number_compare_) { |
+ include_number_compare_name = "_NO_NUMBER"; |
+ } |
+ |
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength), |
+ "CompareStub_%s%s%s%s", |
+ cc_name, |
+ strict_name, |
+ never_nan_nan_name, |
+ include_number_compare_name); |
+ return name_; |
} |
@@ -12227,6 +12435,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)); |
@@ -12324,7 +12535,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) |