Speed up compares with characters ie single-character strings.
Make use of it when we know that something can't be a NaN. Review URL: http://codereview.chromium.org/524059 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3566 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
d73f2efcf3
commit
eb6b3f601a
@ -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<unsigned>(cc_) >> 28 < (1 << 15));
|
||||
return (static_cast<unsigned>(cc_) >> 27) | (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_ != eq) nnn_value = 0; // Avoid duplicate stubs.
|
||||
return (static_cast<unsigned>(cc_) >> 26) | nnn_value | (strict_ ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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<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, 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<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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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<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);
|
||||
}
|
||||
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
50
test/mjsunit/compare-character.js
Normal file
50
test/mjsunit/compare-character.js
Normal file
@ -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, "<r" + x);
|
||||
assertEquals(f >= x, 'f' >= x, "<=r" + x);
|
||||
assertEquals(f < x, 'f' < x, ">r" + x);
|
||||
assertEquals(f <= x, 'f' <= x, ">=r" + x);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user