diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 589c2fb43a..cb474f44f0 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -44,7 +44,8 @@ namespace internal { static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow, - Condition cc); + Condition cc, + bool never_nan_nan); static void EmitSmiNonsmiComparison(MacroAssembler* masm, Label* rhs_not_nan, Label* slow, @@ -4598,47 +4599,55 @@ void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) { // for "identity and not NaN". static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow, - Condition cc) { + Condition cc, + bool never_nan_nan) { Label not_identical; + Label heap_number, return_equal; + Register exp_mask_reg = r5; __ cmp(r0, Operand(r1)); __ b(ne, ¬_identical); - Register exp_mask_reg = r5; - __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask)); + // The two objects are identical. If we know that one of them isn't NaN then + // we now know they test equal. + if (cc != eq || !never_nan_nan) { + __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask)); - // Test for NaN. Sadly, we can't just compare to Factory::nan_value(), - // so we do the second best thing - test it ourselves. - Label heap_number, return_equal; - // They are both equal and they are not both Smis so both of them are not - // Smis. If it's not a heap number, then return equal. - if (cc == lt || cc == gt) { - __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE); - __ b(ge, slow); - } else { - __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE); - __ b(eq, &heap_number); - // Comparing JS objects with <=, >= is complicated. - if (cc != eq) { - __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE)); + // Test for NaN. Sadly, we can't just compare to Factory::nan_value(), + // so we do the second best thing - test it ourselves. + // They are both equal and they are not both Smis so both of them are not + // Smis. If it's not a heap number, then return equal. + if (cc == lt || cc == gt) { + __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE); __ b(ge, slow); - // Normally here we fall through to return_equal, but undefined is - // special: (undefined == undefined) == true, but (undefined <= undefined) - // == false! See ECMAScript 11.8.5. - if (cc == le || cc == ge) { - __ cmp(r4, Operand(ODDBALL_TYPE)); - __ b(ne, &return_equal); - __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - __ cmp(r0, Operand(r2)); - __ b(ne, &return_equal); - if (cc == le) { - __ mov(r0, Operand(GREATER)); // undefined <= undefined should fail. - } else { - __ mov(r0, Operand(LESS)); // undefined >= undefined should fail. + } else { + __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE); + __ b(eq, &heap_number); + // Comparing JS objects with <=, >= is complicated. + if (cc != eq) { + __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE)); + __ b(ge, slow); + // Normally here we fall through to return_equal, but undefined is + // special: (undefined == undefined) == true, but + // (undefined <= undefined) == false! See ECMAScript 11.8.5. + if (cc == le || cc == ge) { + __ cmp(r4, Operand(ODDBALL_TYPE)); + __ b(ne, &return_equal); + __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); + __ cmp(r0, Operand(r2)); + __ b(ne, &return_equal); + if (cc == le) { + // undefined <= undefined should fail. + __ mov(r0, Operand(GREATER)); + } else { + // undefined >= undefined should fail. + __ mov(r0, Operand(LESS)); + } + __ mov(pc, Operand(lr)); // Return. } - __ mov(pc, Operand(lr)); // Return. } } } + __ bind(&return_equal); if (cc == lt) { __ mov(r0, Operand(GREATER)); // Things aren't less than themselves. @@ -4649,43 +4658,45 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm, } __ mov(pc, Operand(lr)); // Return. - // For less and greater we don't have to check for NaN since the result of - // x < x is false regardless. For the others here is some code to check - // for NaN. - if (cc != lt && cc != gt) { - __ 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. - // Read top bits of double representation (second word of value). - __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); - // Test that exponent bits are all set. - __ and_(r3, r2, Operand(exp_mask_reg)); - __ cmp(r3, Operand(exp_mask_reg)); - __ b(ne, &return_equal); + if (cc != eq || !never_nan_nan) { + // For less and greater we don't have to check for NaN since the result of + // x < x is false regardless. For the others here is some code to check + // for NaN. + if (cc != lt && cc != gt) { + __ 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. + // Read top bits of double representation (second word of value). + __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); + // Test that exponent bits are all set. + __ and_(r3, r2, Operand(exp_mask_reg)); + __ cmp(r3, Operand(exp_mask_reg)); + __ b(ne, &return_equal); - // Shift out flag and all exponent bits, retaining only mantissa. - __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord)); - // Or with all low-bits of mantissa. - __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); - __ orr(r0, r3, Operand(r2), SetCC); - // For equal we already have the right value in r0: Return zero (equal) - // if all bits in mantissa are zero (it's an Infinity) and non-zero if not - // (it's a NaN). For <= and >= we need to load r0 with the failing value - // if it's a NaN. - if (cc != eq) { - // All-zero means Infinity means equal. - __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal - if (cc == le) { - __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail. - } else { - __ mov(r0, Operand(LESS)); // NaN >= NaN should fail. + // Shift out flag and all exponent bits, retaining only mantissa. + __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord)); + // Or with all low-bits of mantissa. + __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); + __ orr(r0, r3, Operand(r2), SetCC); + // For equal we already have the right value in r0: Return zero (equal) + // if all bits in mantissa are zero (it's an Infinity) and non-zero if not + // (it's a NaN). For <= and >= we need to load r0 with the failing value + // if it's a NaN. + if (cc != eq) { + // All-zero means Infinity means equal. + __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal + if (cc == le) { + __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail. + } else { + __ mov(r0, Operand(LESS)); // NaN >= NaN should fail. + } } + __ mov(pc, Operand(lr)); // Return. } - __ mov(pc, Operand(lr)); // Return. - } - // No fall through here. + // No fall through here. + } __ bind(¬_identical); } @@ -4885,6 +4896,14 @@ static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) { // Check for oddballs: true, false, null, undefined. __ cmp(r3, Operand(ODDBALL_TYPE)); __ b(eq, &return_not_equal); + + // Now that we have the types we might as well check for symbol-symbol. + // Ensure that no non-strings have the symbol bit set. + ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE); + ASSERT(kSymbolTag != 0); + __ and_(r2, r2, Operand(r3)); + __ tst(r2, Operand(kIsSymbolMask)); + __ b(ne, &return_not_equal); } @@ -4911,12 +4930,13 @@ static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm, // Fast negative check for symbol-to-symbol equality. static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) { // r2 is object type of r0. - __ tst(r2, Operand(kIsNotStringMask)); - __ b(ne, slow); + // Ensure that no non-strings have the symbol bit set. + ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE); + ASSERT(kSymbolTag != 0); __ tst(r2, Operand(kIsSymbolMask)); __ b(eq, slow); - __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE); - __ b(ge, slow); + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset)); __ tst(r3, Operand(kIsSymbolMask)); __ b(eq, slow); @@ -4938,7 +4958,7 @@ void CompareStub::Generate(MacroAssembler* masm) { // Handle the case where the objects are identical. Either returns the answer // or goes to slow. Only falls through if the objects were not identical. - EmitIdenticalObjectComparison(masm, &slow, cc_); + EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_); // If either is a Smi (we know that not both are), then they can only // be strictly equal if the other is a HeapNumber. @@ -5002,7 +5022,9 @@ void CompareStub::Generate(MacroAssembler* masm) { &slow); __ bind(&check_for_symbols); - if (cc_ == eq) { + // In the strict case the EmitStrictTwoHeapObjectCompare already took care of + // symbols. + if (cc_ == eq && !strict_) { // Either jumps to slow or returns the answer. Assumes that r2 is the type // of r0 on entry. EmitCheckForSymbols(masm, &slow); @@ -6487,10 +6509,53 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { } +const char* CompareStub::GetName() { + switch(cc_) { + case lt: return "CompareStub_LT"; + case gt: return "CompareStub_GT"; + case le: return "CompareStub_LE"; + case ge: return "CompareStub_GE"; + case ne: { + 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 eq: { + 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(cc_) >> 28 < (1 << 15)); - return (static_cast(cc_) >> 27) | (strict_ ? 1 : 0); + // Encode the three parameters in a unique 16 bit value. + ASSERT(static_cast(cc_) < (1 << 14)); + int nnn_value = (never_nan_nan_ ? 2 : 0); + if (cc_ != eq) nnn_value = 0; // Avoid duplicate stubs. + return (static_cast(cc_) >> 26) | nnn_value | (strict_ ? 1 : 0); } diff --git a/src/codegen.h b/src/codegen.h index 249eeacf4d..2247c5c80a 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -317,15 +317,30 @@ class GenericUnaryOpStub : public CodeStub { }; +enum NaNInformation { + kBothCouldBeNaN, + kCantBothBeNaN +}; + + class CompareStub: public CodeStub { public: - CompareStub(Condition cc, bool strict) : cc_(cc), strict_(strict) { } + CompareStub(Condition cc, + bool strict, + NaNInformation nan_info = kBothCouldBeNaN) : + cc_(cc), strict_(strict), never_nan_nan_(nan_info == kCantBothBeNaN) { } void Generate(MacroAssembler* masm); private: Condition cc_; bool strict_; + // Only used for 'equal' comparisons. Tells the stub that we already know + // that at least one side of the comparison is not NaN. This allows the + // stub to use object identity in the positive case. We ignore it when + // generating the minor key for other comparisons to avoid creating more + // stubs. + bool never_nan_nan_; Major MajorKey() { return Compare; } @@ -337,6 +352,9 @@ class CompareStub: public CodeStub { Register object, Register scratch); + // 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* GetName(); #ifdef DEBUG void Print() { PrintF("CompareStub (cc %d), (strict %s)\n", diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 386350ffdc..2e85e212a8 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -1899,6 +1899,13 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, } +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 @@ void CodeGenerator::Comparison(AstNode* node, } 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 @@ void CodeGenerator::Comparison(AstNode* node, } // Setup and call the compare stub. - CompareStub stub(cc, strict); + CompareStub stub(cc, strict, kCantBothBeNaN); Result result = frame_->CallStub(&stub, &left_side, &right_side); result.ToRegister(); __ cmp(result.reg(), 0); @@ -2075,18 +2095,150 @@ void CodeGenerator::Comparison(AstNode* node, 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 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, kCantBothBeNaN); + 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)) ? + kBothCouldBeNaN : + kCantBothBeNaN; 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 @@ void CodeGenerator::Comparison(AstNode* node, 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 @@ void CompareStub::Generate(MacroAssembler* masm) { // 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 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } +// 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(cc_) < (1 << 15)); - return (static_cast(cc_) << 1) | (strict_ ? 1 : 0); + // Encode the three parameters in a unique 16 bit value. + ASSERT(static_cast(cc_) < (1 << 14)); + int nnn_value = (never_nan_nan_ ? 2 : 0); + if (cc_ != equal) nnn_value = 0; // Avoid duplicate stubs. + return (static_cast(cc_) << 2) | nnn_value | (strict_ ? 1 : 0); } diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 91950dcab0..5658605aa3 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -313,6 +313,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Is the string a symbol? __ movzx_b(ebx, FieldOperand(edx, Map::kInstanceTypeOffset)); + ASSERT(kSymbolTag != 0); __ test(ebx, Immediate(kIsSymbolMask)); __ j(zero, &slow, not_taken); diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h index 1383e2a7cb..d6d55d12c9 100644 --- a/src/ia32/virtual-frame-ia32.h +++ b/src/ia32/virtual-frame-ia32.h @@ -395,6 +395,8 @@ class VirtualFrame: public ZoneObject { // Pushing a result invalidates it (its contents become owned by the // frame). void Push(Result* result) { + // This assert will trigger if you try to push the same value twice. + ASSERT(result->is_valid()); if (result->is_register()) { Push(result->reg()); } else { diff --git a/src/objects-inl.h b/src/objects-inl.h index b72ec17b5b..3003342460 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -150,8 +150,12 @@ bool Object::IsString() { bool Object::IsSymbol() { if (!this->IsHeapObject()) return false; uint32_t type = HeapObject::cast(this)->map()->instance_type(); - return (type & (kIsNotStringMask | kIsSymbolMask)) == - (kStringTag | kSymbolTag); + // Because the symbol tag is non-zero and no non-string types have the + // symbol bit set we can test for symbols with a very simple test + // operation. + ASSERT(kSymbolTag != 0); + ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE); + return (type & kIsSymbolMask) != 0; } @@ -226,7 +230,8 @@ StringShape::StringShape(InstanceType t) bool StringShape::IsSymbol() { ASSERT(valid()); - return (type_ & kIsSymbolMask) == kSymbolTag; + ASSERT(kSymbolTag != 0); + return (type_ & kIsSymbolMask) != 0; } diff --git a/src/objects.h b/src/objects.h index b4d638d410..5d088e5231 100644 --- a/src/objects.h +++ b/src/objects.h @@ -383,11 +383,12 @@ const uint32_t kIsNotStringMask = 0x80; const uint32_t kStringTag = 0x0; const uint32_t kNotStringTag = 0x80; -// If bit 7 is clear, bit 5 indicates that the string is a symbol (if set) or -// not (if cleared). -const uint32_t kIsSymbolMask = 0x20; +// Bit 6 indicates that the object is a symbol (if set) or not (if cleared). +// There are not enough types that the non-string types (with bit 7 set) can +// have bit 6 set too. +const uint32_t kIsSymbolMask = 0x40; const uint32_t kNotSymbolTag = 0x0; -const uint32_t kSymbolTag = 0x20; +const uint32_t kSymbolTag = 0x40; // If bit 7 is clear then bit 2 indicates whether the string consists of // two-byte characters or one-byte characters. diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 737ba83b49..c88d151282 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -6264,34 +6264,39 @@ void CompareStub::Generate(MacroAssembler* masm) { // 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(rdx, HeapObject::kMapOffset), - Factory::heap_number_map()); - __ j(equal, &heap_number); - __ bind(&return_equal); - __ xor_(rax, rax); - __ ret(0); + if (never_nan_nan_) { + __ xor_(rax, rax); + __ ret(0); + } else { + Label return_equal; + Label heap_number; + // If it's not a heap number, then return equal. + __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), + Factory::heap_number_map()); + __ j(equal, &heap_number); + __ bind(&return_equal); + __ xor_(rax, rax); + __ 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 allow QNaNs, which have bit 51 set (which also rules out - // the value being Infinity). + __ 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 allow QNaNs, which have bit 51 set (which also rules out + // the value being Infinity). - // 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); - __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset)); - __ xorl(rax, rax); - __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits. - __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1)); - __ setcc(above_equal, rax); - __ 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); + __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset)); + __ xorl(rax, rax); + __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits. + __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1)); + __ setcc(above_equal, rax); + __ ret(0); + } __ bind(¬_identical); } @@ -6438,9 +6443,11 @@ void CompareStub::BranchIfNonSymbol(MacroAssembler* masm, __ movq(scratch, FieldOperand(object, HeapObject::kMapOffset)); __ movzxbq(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); - __ and_(scratch, Immediate(kIsSymbolMask | kIsNotStringMask)); - __ cmpb(scratch, Immediate(kSymbolTag | kStringTag)); - __ j(not_equal, label); + // Ensure that no non-strings have the symbol bit set. + ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE); + ASSERT(kSymbolTag != 0); + __ testb(scratch, Immediate(kIsSymbolMask)); + __ j(zero, label); } @@ -7740,9 +7747,52 @@ void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) { int CompareStub::MinorKey() { - // Encode the two parameters in a unique 16 bit value. - ASSERT(static_cast(cc_) < (1 << 15)); - return (static_cast(cc_) << 1) | (strict_ ? 1 : 0); + // Encode the three parameters in a unique 16 bit value. + ASSERT(static_cast(cc_) < (1 << 14)); + int nnn_value = (never_nan_nan_ ? 2 : 0); + if (cc_ != equal) nnn_value = 0; // Avoid duplicate stubs. + return (static_cast(cc_) << 2) | nnn_value | (strict_ ? 1 : 0); +} + + +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"; + } } diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index edcac2bbac..a0f87ad4a2 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -330,6 +330,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Is the string a symbol? __ j(not_zero, &index_string); // The value in rbx is used at jump target. + ASSERT(kSymbolTag != 0); __ testb(FieldOperand(rdx, Map::kInstanceTypeOffset), Immediate(kIsSymbolMask)); __ j(zero, &slow); diff --git a/test/mjsunit/bit-not.js b/test/mjsunit/bit-not.js index e7fdd990d7..85eccc4a29 100644 --- a/test/mjsunit/bit-not.js +++ b/test/mjsunit/bit-not.js @@ -25,43 +25,43 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -function testBitNot(x) { +function testBitNot(x, name) { // The VM constant folds so we use that to check the result. var expected = eval("~(" + x + ")"); var actual = ~x; - assertEquals(expected, actual, "x: " + x); + assertEquals(expected, actual, "x: " + name); // Test the path where we can overwrite the result. Use - // to avoid concatenating strings. expected = eval("~(" + x + " - 0.01)"); actual = ~(x - 0.01); - assertEquals(expected, actual, "x - 0.01: " + x); + assertEquals(expected, actual, "x - 0.01: " + name); } -testBitNot(0); -testBitNot(1); -testBitNot(-1); -testBitNot(100); -testBitNot(0x40000000); -testBitNot(0x7fffffff); -testBitNot(0x80000000); +testBitNot(0, 0); +testBitNot(1, 1); +testBitNot(-1, 1); +testBitNot(100, 100); +testBitNot(0x40000000, "0x40000000"); +testBitNot(0x7fffffff, "0x7fffffff"); +testBitNot(0x80000000, "0x80000000"); -testBitNot(2.2); -testBitNot(-2.3); -testBitNot(Infinity); -testBitNot(NaN); -testBitNot(-Infinity); -testBitNot(0x40000000 + 0.12345); -testBitNot(0x40000000 - 0.12345); -testBitNot(0x7fffffff + 0.12345); -testBitNot(0x7fffffff - 0.12345); -testBitNot(0x80000000 + 0.12345); -testBitNot(0x80000000 - 0.12345); +testBitNot(2.2, 2.2); +testBitNot(-2.3, -2.3); +testBitNot(Infinity, "Infinity"); +testBitNot(NaN, "NaN"); +testBitNot(-Infinity, "-Infinity"); +testBitNot(0x40000000 + 0.12345, "float1"); +testBitNot(0x40000000 - 0.12345, "float2"); +testBitNot(0x7fffffff + 0.12345, "float3"); +testBitNot(0x7fffffff - 0.12345, "float4"); +testBitNot(0x80000000 + 0.12345, "float5"); +testBitNot(0x80000000 - 0.12345, "float6"); -testBitNot("0"); -testBitNot("2.3"); -testBitNot("-9.4"); +testBitNot("0", "string0"); +testBitNot("2.3", "string2.3"); +testBitNot("-9.4", "string-9.4"); // Try to test that we can deal with allocation failures in diff --git a/test/mjsunit/compare-character.js b/test/mjsunit/compare-character.js new file mode 100644 index 0000000000..cabe0137bf --- /dev/null +++ b/test/mjsunit/compare-character.js @@ -0,0 +1,50 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test the optimized implementation of comparison with single-character +// strings. + +var a = ['', String.fromCharCode(0), ' ', 'e', 'erik', 'f', 'foo', 'g', 'goo', + -1, 0, 1, 1.2, -7.9, true, false, 'foo', '0', 'NaN' ]; +for (var i in a) { + var x = a[i]; + var f = 'f'; + + assertEquals(x == f, x == 'f', "==" + x); + assertEquals(x === f, x === 'f', "===" + x); + assertEquals(x < f, x < 'f', "<" + x); + assertEquals(x <= f, x <= 'f', "<=" + x); + assertEquals(x > f, x > 'f', ">" + x); + assertEquals(x >= f, x >= 'f', ">=" + x); + assertEquals(f == x, 'f' == x, "==r" + x); + assertEquals(f === x, 'f' === x, "===r" + x); + assertEquals(f > x, 'f' > x, "= x, 'f' >= x, "<=r" + x); + assertEquals(f < x, 'f' < x, ">r" + x); + assertEquals(f <= x, 'f' <= x, ">=r" + x); +} + diff --git a/test/mjsunit/compare-nan.js b/test/mjsunit/compare-nan.js index fc40acc049..c4f7817ff8 100644 --- a/test/mjsunit/compare-nan.js +++ b/test/mjsunit/compare-nan.js @@ -42,3 +42,25 @@ for (var i in a) { assertFalse(x <= NaN, "" + x + " <= NaN"); assertFalse(x >= NaN, "" + x + " >= NaN"); } + +var b = ["NaN", "-1", "0", "1", "1.2", "-7.9", "true", "false", "'foo'", "'0'", + "'NaN'" ]; +for (var i in b) { + var x = b[i]; + var program = + "assertFalse(NaN == " + x + ", 'NaN == ' + " + x + ");\n" + + "assertFalse(NaN === " + x + ", 'NaN === ' + " + x + ");\n" + + "assertFalse(NaN < " + x + ", 'NaN < ' + " + x + ");\n" + + "assertFalse(NaN > " + x + ", 'NaN > ' + " + x + ");\n" + + "assertFalse(NaN <= " + x + ", 'NaN <= ' + " + x + ");\n" + + "assertFalse(NaN >= " + x + ", 'NaN >= ' + " + x + ");\n" + + + "assertFalse(" + x + " == NaN, '' + " + x + " + ' == NaN');\n" + + "assertFalse(" + x + " === NaN, '' + " + x + " + ' === NaN');\n" + + "assertFalse(" + x + " < NaN, '' + " + x + " + ' < NaN');\n" + + "assertFalse(" + x + " > NaN, '' + " + x + " + ' > NaN');\n" + + "assertFalse(" + x + " <= NaN, '' + " + x + " + ' <= NaN');\n" + + "assertFalse(" + x + " >= NaN, '' + " + x + " + ' >= NaN');\n"; + eval(program); +} +