Commit eb6b3f60 authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

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
parent d73f2efc
......@@ -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,17 +4599,21 @@ 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, &not_identical);
Register exp_mask_reg = r5;
// 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) {
......@@ -4622,8 +4627,8 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
__ 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.
// 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);
......@@ -4631,14 +4636,18 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
__ cmp(r0, Operand(r2));
__ b(ne, &return_equal);
if (cc == le) {
__ mov(r0, Operand(GREATER)); // undefined <= undefined should fail.
// undefined <= undefined should fail.
__ mov(r0, Operand(GREATER));
} else {
__ mov(r0, Operand(LESS)); // undefined >= undefined should fail.
// undefined >= undefined should fail.
__ mov(r0, Operand(LESS));
}
__ mov(pc, Operand(lr)); // Return.
}
}
}
}
__ bind(&return_equal);
if (cc == lt) {
__ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
......@@ -4649,6 +4658,7 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
}
__ mov(pc, Operand(lr)); // Return.
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.
......@@ -4686,6 +4696,7 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
__ mov(pc, Operand(lr)); // Return.
}
// No fall through here.
}
__ bind(&not_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",
......
This diff is collapsed.
......@@ -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,6 +6264,10 @@ 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.
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.
......@@ -6275,8 +6279,8 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ 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.
// 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
......@@ -6292,6 +6296,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
__ setcc(above_equal, rax);
__ ret(0);
}
__ bind(&not_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
......
// 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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment