| Index: src/ia32/codegen-ia32.cc
|
| ===================================================================
|
| --- src/ia32/codegen-ia32.cc (revision 3561)
|
| +++ src/ia32/codegen-ia32.cc (working copy)
|
| @@ -1899,6 +1899,13 @@
|
| }
|
|
|
|
|
| +static bool CouldBeNaN(const Result& result) {
|
| + if (!result.is_constant()) return true;
|
| + if (!result.handle()->IsHeapNumber()) return false;
|
| + return isnan(HeapNumber::cast(*result.handle())->value());
|
| +}
|
| +
|
| +
|
| void CodeGenerator::Comparison(AstNode* node,
|
| Condition cc,
|
| bool strict,
|
| @@ -1919,15 +1926,28 @@
|
| }
|
| ASSERT(cc == less || cc == equal || cc == greater_equal);
|
|
|
| - // If either side is a constant smi, optimize the comparison.
|
| - bool left_side_constant_smi =
|
| - left_side.is_constant() && left_side.handle()->IsSmi();
|
| - bool right_side_constant_smi =
|
| - right_side.is_constant() && right_side.handle()->IsSmi();
|
| - bool left_side_constant_null =
|
| - left_side.is_constant() && left_side.handle()->IsNull();
|
| - bool right_side_constant_null =
|
| - right_side.is_constant() && right_side.handle()->IsNull();
|
| + // If either side is a constant of some sort, we can probably optimize the
|
| + // comparison.
|
| + bool left_side_constant_smi = false;
|
| + bool left_side_constant_null = false;
|
| + bool left_side_constant_1_char_string = false;
|
| + if (left_side.is_constant()) {
|
| + left_side_constant_smi = left_side.handle()->IsSmi();
|
| + left_side_constant_null = left_side.handle()->IsNull();
|
| + left_side_constant_1_char_string =
|
| + (left_side.handle()->IsString() &&
|
| + (String::cast(*left_side.handle())->length() == 1));
|
| + }
|
| + bool right_side_constant_smi = false;
|
| + bool right_side_constant_null = false;
|
| + bool right_side_constant_1_char_string = false;
|
| + if (right_side.is_constant()) {
|
| + right_side_constant_smi = right_side.handle()->IsSmi();
|
| + right_side_constant_null = right_side.handle()->IsNull();
|
| + right_side_constant_1_char_string =
|
| + (right_side.handle()->IsString() &&
|
| + (String::cast(*right_side.handle())->length() == 1));
|
| + }
|
|
|
| if (left_side_constant_smi || right_side_constant_smi) {
|
| if (left_side_constant_smi && right_side_constant_smi) {
|
| @@ -2016,7 +2036,7 @@
|
| }
|
|
|
| // Setup and call the compare stub.
|
| - CompareStub stub(cc, strict);
|
| + CompareStub stub(cc, strict, CantBothBeNaN);
|
| Result result = frame_->CallStub(&stub, &left_side, &right_side);
|
| result.ToRegister();
|
| __ cmp(result.reg(), 0);
|
| @@ -2075,18 +2095,150 @@
|
| operand.Unuse();
|
| dest->Split(not_zero);
|
| }
|
| + } else if (left_side_constant_1_char_string ||
|
| + right_side_constant_1_char_string) {
|
| + if (left_side_constant_1_char_string && right_side_constant_1_char_string) {
|
| + // Trivial case, comparing two constants.
|
| + int left_value = String::cast(*left_side.handle())->Get(0);
|
| + int right_value = String::cast(*right_side.handle())->Get(0);
|
| + switch (cc) {
|
| + case less:
|
| + dest->Goto(left_value < right_value);
|
| + break;
|
| + case equal:
|
| + dest->Goto(left_value == right_value);
|
| + break;
|
| + case greater_equal:
|
| + dest->Goto(left_value >= right_value);
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + } else {
|
| + // Only one side is a constant 1 character string.
|
| + // If left side is a constant 1-character string, reverse the operands.
|
| + // Since one side is a constant string, conversion order does not matter.
|
| + if (left_side_constant_1_char_string) {
|
| + Result temp = left_side;
|
| + left_side = right_side;
|
| + right_side = temp;
|
| + cc = ReverseCondition(cc);
|
| + // This may reintroduce 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 string, inlining the case
|
| + // where both sides are strings.
|
| + left_side.ToRegister();
|
| +
|
| + // Here we split control flow to the stub call and inlined cases
|
| + // before finally splitting it to the control destination. We use
|
| + // a jump target and branching to duplicate the virtual frame at
|
| + // the first split. We manually handle the off-frame references
|
| + // by reconstituting them on the non-fall-through path.
|
| + JumpTarget is_not_string, is_string;
|
| + Register left_reg = left_side.reg();
|
| + Handle<Object> right_val = right_side.handle();
|
| + __ test(left_side.reg(), Immediate(kSmiTagMask));
|
| + is_not_string.Branch(zero, &left_side);
|
| + Result temp = allocator_->Allocate();
|
| + ASSERT(temp.is_valid());
|
| + __ mov(temp.reg(),
|
| + FieldOperand(left_side.reg(), HeapObject::kMapOffset));
|
| + __ movzx_b(temp.reg(),
|
| + FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
|
| + // If we are testing for equality then make use of the symbol shortcut.
|
| + // Check if the right left hand side has the same type as the left hand
|
| + // side (which is always a symbol).
|
| + if (cc == equal) {
|
| + Label not_a_symbol;
|
| + ASSERT(kSymbolTag != 0);
|
| + // Ensure that no non-strings have the symbol bit set.
|
| + ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
|
| + __ test(temp.reg(), Immediate(kIsSymbolMask)); // Test the symbol bit.
|
| + __ j(zero, ¬_a_symbol);
|
| + // They are symbols, so do identity compare.
|
| + __ cmp(left_side.reg(), right_side.handle());
|
| + dest->true_target()->Branch(equal);
|
| + dest->false_target()->Branch(not_equal);
|
| + __ bind(¬_a_symbol);
|
| + }
|
| + // If the receiver is not a string of the type we handle call the stub.
|
| + __ and_(temp.reg(),
|
| + kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
|
| + __ cmp(temp.reg(), kStringTag | kSeqStringTag | kAsciiStringTag);
|
| + temp.Unuse();
|
| + is_string.Branch(equal, &left_side);
|
| +
|
| + // Setup and call the compare stub.
|
| + is_not_string.Bind(&left_side);
|
| + CompareStub stub(cc, strict, CantBothBeNaN);
|
| + Result result = frame_->CallStub(&stub, &left_side, &right_side);
|
| + result.ToRegister();
|
| + __ cmp(result.reg(), 0);
|
| + result.Unuse();
|
| + dest->true_target()->Branch(cc);
|
| + dest->false_target()->Jump();
|
| +
|
| + is_string.Bind(&left_side);
|
| + // Here we know we have a sequential ASCII string.
|
| + left_side = Result(left_reg);
|
| + right_side = Result(right_val);
|
| + Result temp2 = allocator_->Allocate();
|
| + ASSERT(temp2.is_valid());
|
| + // Test string equality and comparison.
|
| + if (cc == equal) {
|
| + Label comparison_done;
|
| + __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
|
| + Immediate(1));
|
| + __ j(not_equal, &comparison_done);
|
| + __ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
|
| + String::cast(*right_side.handle())->Get(0));
|
| + __ bind(&comparison_done);
|
| + } else {
|
| + __ mov(temp2.reg(),
|
| + FieldOperand(left_side.reg(), String::kLengthOffset));
|
| + __ sub(Operand(temp2.reg()), Immediate(1));
|
| + Label comparison;
|
| + // If the length is 0 then our subtraction gave -1 which compares less
|
| + // than any character.
|
| + __ j(negative, &comparison);
|
| + // Otherwise load the first character.
|
| + __ movzx_b(temp2.reg(),
|
| + FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize));
|
| + __ bind(&comparison);
|
| + // Compare the first character of the string with out constant
|
| + // 1-character string.
|
| + __ cmp(Operand(temp2.reg()),
|
| + Immediate(String::cast(*right_side.handle())->Get(0)));
|
| + Label characters_were_different;
|
| + __ j(not_equal, &characters_were_different);
|
| + // If the first character is the same then the long string sorts after
|
| + // the short one.
|
| + __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
|
| + Immediate(1));
|
| + __ bind(&characters_were_different);
|
| + }
|
| + temp2.Unuse();
|
| + left_side.Unuse();
|
| + right_side.Unuse();
|
| + dest->Split(cc);
|
| + }
|
| } else {
|
| // Neither side is a constant Smi or null.
|
| // If either side is a non-smi constant, skip the smi check.
|
| bool known_non_smi =
|
| (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
|
| (right_side.is_constant() && !right_side.handle()->IsSmi());
|
| + NaNInformation nan_info =
|
| + (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
|
| + BothCouldBeNaN :
|
| + CantBothBeNaN;
|
| left_side.ToRegister();
|
| right_side.ToRegister();
|
|
|
| if (known_non_smi) {
|
| // When non-smi, call out to the compare stub.
|
| - CompareStub stub(cc, strict);
|
| + CompareStub stub(cc, strict, nan_info);
|
| Result answer = frame_->CallStub(&stub, &left_side, &right_side);
|
| if (cc == equal) {
|
| __ test(answer.reg(), Operand(answer.reg()));
|
| @@ -2113,7 +2265,7 @@
|
| temp.Unuse();
|
| is_smi.Branch(zero, taken);
|
| // When non-smi, call out to the compare stub.
|
| - CompareStub stub(cc, strict);
|
| + CompareStub stub(cc, strict, nan_info);
|
| Result answer = frame_->CallStub(&stub, &left_side, &right_side);
|
| if (cc == equal) {
|
| __ test(answer.reg(), Operand(answer.reg()));
|
| @@ -8229,35 +8381,41 @@
|
| // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
|
| // so we do the second best thing - test it ourselves.
|
|
|
| - Label return_equal;
|
| - Label heap_number;
|
| - // If it's not a heap number, then return equal.
|
| - __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
|
| - Immediate(Factory::heap_number_map()));
|
| - __ j(equal, &heap_number);
|
| - __ bind(&return_equal);
|
| - __ Set(eax, Immediate(0));
|
| - __ ret(0);
|
| + if (never_nan_nan_) {
|
| + __ Set(eax, Immediate(0));
|
| + __ ret(0);
|
| + } else {
|
| + Label return_equal;
|
| + Label heap_number;
|
| + // If it's not a heap number, then return equal.
|
| + __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
|
| + Immediate(Factory::heap_number_map()));
|
| + __ j(equal, &heap_number);
|
| + __ bind(&return_equal);
|
| + __ Set(eax, Immediate(0));
|
| + __ ret(0);
|
|
|
| - __ bind(&heap_number);
|
| - // It is a heap number, so return non-equal if it's NaN and equal if it's
|
| - // not NaN.
|
| - // The representation of NaN values has all exponent bits (52..62) set,
|
| - // and not all mantissa bits (0..51) clear.
|
| - // We only accept QNaNs, which have bit 51 set.
|
| - // Read top bits of double representation (second word of value).
|
| + __ bind(&heap_number);
|
| + // It is a heap number, so return non-equal if it's NaN and equal if
|
| + // it's not NaN.
|
| + // The representation of NaN values has all exponent bits (52..62) set,
|
| + // and not all mantissa bits (0..51) clear.
|
| + // We only accept QNaNs, which have bit 51 set.
|
| + // Read top bits of double representation (second word of value).
|
|
|
| - // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
|
| - // all bits in the mask are set. We only need to check the word
|
| - // that contains the exponent and high bit of the mantissa.
|
| - ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
|
| - __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
|
| - __ xor_(eax, Operand(eax));
|
| - // Shift value and mask so kQuietNaNHighBitsMask applies to topmost bits.
|
| - __ add(edx, Operand(edx));
|
| - __ cmp(edx, kQuietNaNHighBitsMask << 1);
|
| - __ setcc(above_equal, eax);
|
| - __ ret(0);
|
| + // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
|
| + // all bits in the mask are set. We only need to check the word
|
| + // that contains the exponent and high bit of the mantissa.
|
| + ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
|
| + __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
|
| + __ xor_(eax, Operand(eax));
|
| + // Shift value and mask so kQuietNaNHighBitsMask applies to topmost
|
| + // bits.
|
| + __ add(edx, Operand(edx));
|
| + __ cmp(edx, kQuietNaNHighBitsMask << 1);
|
| + __ setcc(above_equal, eax);
|
| + __ ret(0);
|
| + }
|
|
|
| __ bind(¬_identical);
|
| }
|
| @@ -8982,10 +9140,55 @@
|
| }
|
|
|
|
|
| +// 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() {
|
| + 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";
|
| + }
|
| +}
|
| +
|
| +
|
| int CompareStub::MinorKey() {
|
| - // Encode the two parameters in a unique 16 bit value.
|
| - ASSERT(static_cast<unsigned>(cc_) < (1 << 15));
|
| - return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0);
|
| + // Encode the three parameters in a unique 16 bit value.
|
| + ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
|
| + int nnn_value = (never_nan_nan_ ? 2 : 0);
|
| + if (cc_ != equal) nnn_value = 0; // Avoid duplicate stubs.
|
| + return (static_cast<unsigned>(cc_) << 2) | nnn_value | (strict_ ? 1 : 0);
|
| }
|
|
|
|
|
|
|