From 1c89cef32d5ddef600aaa1df62a635fcda374c27 Mon Sep 17 00:00:00 2001 From: "kasperl@chromium.org" Date: Fri, 10 Oct 2008 06:26:35 +0000 Subject: [PATCH] Make strict equality checks faster on IA32 by doing more work in the CompareStub. I'll port this to ARM once I'm satisfied with the performance improvements. Review URL: http://codereview.chromium.org/7014 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@481 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/codegen-ia32.cc | 77 +++++++++++++++++++++++++++++++++++++++++++-- src/runtime.js | 21 ++++++------- 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc index 34a3cdd825..c0d081f09a 100644 --- a/src/codegen-ia32.cc +++ b/src/codegen-ia32.cc @@ -1127,9 +1127,8 @@ void CodeGenerator::Comparison(Condition cc, bool strict) { __ pop(edx); } + // Check for the smi case. Label is_smi, done; - CompareStub stub(cc, strict); - __ mov(ecx, Operand(eax)); __ or_(ecx, Operand(edx)); __ test(ecx, Immediate(kSmiTagMask)); @@ -1137,8 +1136,13 @@ void CodeGenerator::Comparison(Condition cc, bool strict) { // When non-smi, call out to the compare stub. "parameters" setup by // calling code in edx and eax and "result" is returned in the flags. + CompareStub stub(cc, strict); __ CallStub(&stub); - __ cmp(eax, 0); + if (cc == equal) { + __ test(eax, Operand(eax)); + } else { + __ cmp(eax, 0); + } __ jmp(&done); // Test smi equality by pointer comparison. @@ -4484,6 +4488,73 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { void CompareStub::Generate(MacroAssembler* masm) { Label call_builtin, done; + + // If we're doing a strict equality comparison, we generate code + // to do fast comparison for objects and oddballs. Numbers and + // strings still go through the usual slow-case code. + if (strict_) { + Label slow; + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &slow); + + // Get the type of the first operand. + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + + // If the first object is an object, we do pointer comparison. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + Label non_object; + __ cmp(ecx, FIRST_JS_OBJECT_TYPE); + __ j(less, &non_object); + __ sub(eax, Operand(edx)); + __ ret(0); + + // Check for oddballs: true, false, null, undefined. + __ bind(&non_object); + __ cmp(ecx, ODDBALL_TYPE); + __ j(not_equal, &slow); + + // If the oddball isn't undefined, we do pointer comparison. For + // the undefined value, we have to be careful and check for + // 'undetectable' objects too. + Label undefined; + __ cmp(Operand(eax), Immediate(Factory::undefined_value())); + __ j(equal, &undefined); + __ sub(eax, Operand(edx)); + __ ret(0); + + // Undefined case: If the other operand isn't undefined too, we + // have to check if it's 'undetectable'. + Label check_undetectable; + __ bind(&undefined); + __ cmp(Operand(edx), Immediate(Factory::undefined_value())); + __ j(not_equal, &check_undetectable); + __ Set(eax, Immediate(0)); + __ ret(0); + + // Check for undetectability of the other operand. + Label not_strictly_equal; + __ bind(&check_undetectable); + __ test(edx, Immediate(kSmiTagMask)); + __ j(zero, ¬_strictly_equal); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset)); + __ and_(ecx, 1 << Map::kIsUndetectable); + __ cmp(ecx, 1 << Map::kIsUndetectable); + __ j(not_equal, ¬_strictly_equal); + __ Set(eax, Immediate(0)); + __ ret(0); + + // No cigar: Objects aren't strictly equal. Register eax contains + // a non-smi value so it can't be 0. Just return. + ASSERT(kHeapTag != 0); + __ bind(¬_strictly_equal); + __ ret(0); + + // Fall through to the general case. + __ bind(&slow); + } + // Save the return address (and get it off the stack). __ pop(ecx); diff --git a/src/runtime.js b/src/runtime.js index a9e58117d8..e44a1039f0 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -91,26 +91,23 @@ function EQUALS(y) { // ECMA-262, section 11.9.4, page 56. function STRICT_EQUALS(x) { - if (IS_NUMBER(this)) { - if (!IS_NUMBER(x)) return 1; // not equal - return %NumberEquals(this, x); - } - if (IS_STRING(this)) { if (!IS_STRING(x)) return 1; // not equal return %StringEquals(this, x); - } + } - if (IS_BOOLEAN(this)) { - if (!IS_BOOLEAN(x)) return 1; // not equal - if (this) return x ? 0 : 1; - else return x ? 1 : 0; - } + if (IS_NUMBER(this)) { + if (!IS_NUMBER(x)) return 1; // not equal + return %NumberEquals(this, x); + } - if (IS_UNDEFINED(this)) { // both undefined and undetectable + if (IS_UNDEFINED(this)) { + // Both undefined and undetectable. return IS_UNDEFINED(x) ? 0 : 1; } + // Objects, null, booleans and functions are all that's left. + // They can all be compared with a simple identity check. return %_ObjectEquals(this, x) ? 0 : 1; }