Author: [EMAIL PROTECTED]
Date: Wed Oct 8 06:33:16 2008
New Revision: 470
Modified:
branches/bleeding_edge/src/code-stubs.cc
branches/bleeding_edge/src/code-stubs.h
branches/bleeding_edge/src/codegen-arm.cc
branches/bleeding_edge/src/codegen-ia32.cc
branches/bleeding_edge/src/macro-assembler-ia32.cc
branches/bleeding_edge/src/macro-assembler-ia32.h
branches/bleeding_edge/src/runtime.js
branches/bleeding_edge/src/stub-cache-ia32.cc
branches/bleeding_edge/test/mjsunit/instanceof.js
Log:
Improve the generated code for the instanceof operator,
and extended the instanceof test case.
Review URL: http://codereview.chromium.org/6341
Modified: branches/bleeding_edge/src/code-stubs.cc
==============================================================================
--- branches/bleeding_edge/src/code-stubs.cc (original)
+++ branches/bleeding_edge/src/code-stubs.cc Wed Oct 8 06:33:16 2008
@@ -110,6 +110,8 @@
return "RevertToNumber";
case ToBoolean:
return "ToBoolean";
+ case Instanceof:
+ return "Instanceof";
case CounterOp:
return "CounterOp";
case ArgumentsAccess:
Modified: branches/bleeding_edge/src/code-stubs.h
==============================================================================
--- branches/bleeding_edge/src/code-stubs.h (original)
+++ branches/bleeding_edge/src/code-stubs.h Wed Oct 8 06:33:16 2008
@@ -44,6 +44,7 @@
UnarySub,
RevertToNumber,
ToBoolean,
+ Instanceof,
CounterOp,
ArgumentsAccess,
Runtime,
@@ -82,7 +83,7 @@
virtual int MinorKey() = 0;
// Returns a name for logging/debugging purposes.
- virtual const char* GetName() = 0;
+ virtual const char* GetName() { return MajorName(MajorKey()); }
#ifdef DEBUG
virtual void Print() { PrintF("%s\n", GetName()); }
Modified: branches/bleeding_edge/src/codegen-arm.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-arm.cc (original)
+++ branches/bleeding_edge/src/codegen-arm.cc Wed Oct 8 06:33:16 2008
@@ -887,8 +887,6 @@
Major MajorKey() { return GetProperty; }
int MinorKey() { return 0; }
void Generate(MacroAssembler* masm);
-
- const char* GetName() { return "GetPropertyStub"; }
};
@@ -900,8 +898,6 @@
Major MajorKey() { return SetProperty; }
int MinorKey() { return 0; }
void Generate(MacroAssembler* masm);
-
- const char* GetName() { return "GetPropertyStub"; }
};
@@ -951,8 +947,6 @@
int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); }
void Generate(MacroAssembler* masm);
- const char* GetName() { return "InvokeBuiltinStub"; }
-
#ifdef DEBUG
void Print() {
PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n",
@@ -1294,8 +1288,6 @@
private:
int argc_;
- const char* GetName() { return "CallFuntionStub"; }
-
#if defined(DEBUG)
void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
#endif // defined(DEBUG)
@@ -3386,7 +3378,8 @@
case Token::INSTANCEOF:
__ mov(r0, Operand(1)); // not counting receiver
__ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS);
- __ push(r0);
+ __ tst(r0, Operand(r0));
+ cc_reg_ = eq;
break;
default:
Modified: branches/bleeding_edge/src/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-ia32.cc (original)
+++ branches/bleeding_edge/src/codegen-ia32.cc Wed Oct 8 06:33:16 2008
@@ -894,18 +894,8 @@
void Generate(MacroAssembler* masm);
private:
-
Major MajorKey() { return ToBoolean; }
-
int MinorKey() { return 0; }
-
- const char* GetName() { return "ToBooleanStub"; }
-
-#ifdef DEBUG
- void Print() {
- PrintF("ToBooleanStub\n");
- }
-#endif
};
@@ -1476,8 +1466,6 @@
return (static_cast<int>(cc_) << 1) | (strict_ ? 1 : 0);
}
- const char* GetName() { return "CompareStub"; }
-
#ifdef DEBUG
void Print() {
PrintF("CompareStub (cc %d), (strict %s)\n",
@@ -1585,8 +1573,6 @@
private:
int argc_;
- const char* GetName() { return "CallFunctionStub"; }
-
#ifdef DEBUG
void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
#endif
@@ -3521,8 +3507,6 @@
int MinorKey() { return is_increment_ ? 1 : 0; }
void Generate(MacroAssembler* masm);
- const char* GetName() { return "RevertToNumberStub"; }
-
#ifdef DEBUG
void Print() {
PrintF("RevertToNumberStub (is_increment %s)\n",
@@ -3552,8 +3536,6 @@
}
void Generate(MacroAssembler* masm);
- const char* GetName() { return "CounterOpStub"; }
-
#ifdef DEBUG
void Print() {
PrintF("CounterOpStub (result_offset %d), (is_postfix %s),"
@@ -3754,6 +3736,18 @@
}
+class InstanceofStub: public CodeStub {
+ public:
+ InstanceofStub() { }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return Instanceof; }
+ int MinorKey() { return 0; }
+};
+
+
void Ia32CodeGenerator::VisitCompareOperation(CompareOperation* node) {
Comment cmnt(masm_, "[ CompareOperation");
@@ -3934,8 +3928,10 @@
case Token::INSTANCEOF: {
Load(left);
Load(right);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
- __ push(eax); // push the result
+ InstanceofStub stub;
+ __ CallStub(&stub);
+ __ test(eax, Operand(eax));
+ cc_reg_ = zero;
return;
}
default:
@@ -5286,6 +5282,53 @@
// Restore frame pointer and return.
__ pop(ebp);
__ ret(0);
+}
+
+
+void InstanceofStub::Generate(MacroAssembler* masm) {
+ // Get the object - go slow case if it's a smi.
+ Label slow;
+ __ mov(eax, Operand(esp, 2 * kPointerSize)); // 2 ~ return address,
function
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &slow, not_taken);
+
+ // Check that the left hand is a JS object.
+ __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // ebx - object
map
+ __ movzx_b(ecx, FieldOperand(eax, Map::kInstanceTypeOffset)); // ecx -
type
+ __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
+ __ j(less, &slow, not_taken);
+ __ cmp(ecx, LAST_JS_OBJECT_TYPE);
+ __ j(greater, &slow, not_taken);
+
+ // Get the prototype of the function.
+ __ mov(edx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
+ __ TryGetFunctionPrototype(edx, ebx, ecx, &slow);
+
+ // Register mapping: eax is object map and ebx is function prototype.
+ __ mov(ecx, FieldOperand(eax, Map::kPrototypeOffset));
+
+ // Loop through the prototype chain looking for the function prototype.
+ Label loop, is_instance, is_not_instance;
+ __ bind(&loop);
+ __ cmp(ecx, Operand(ebx));
+ __ j(equal, &is_instance);
+ __ cmp(Operand(ecx), Immediate(Factory::null_value()));
+ __ j(equal, &is_not_instance);
+ __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ mov(ecx, FieldOperand(ecx, Map::kPrototypeOffset));
+ __ jmp(&loop);
+
+ __ bind(&is_instance);
+ __ Set(eax, Immediate(0));
+ __ ret(2 * kPointerSize);
+
+ __ bind(&is_not_instance);
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ __ ret(2 * kPointerSize);
+
+ // Slow-case: Go through the JavaScript implementation.
+ __ bind(&slow);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
}
Modified: branches/bleeding_edge/src/macro-assembler-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/macro-assembler-ia32.cc (original)
+++ branches/bleeding_edge/src/macro-assembler-ia32.cc Wed Oct 8 06:33:16
2008
@@ -99,8 +99,6 @@
Register addr_;
Register scratch_;
- const char* GetName() { return "RecordWriteStub"; }
-
#ifdef DEBUG
void Print() {
PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch
reg %d)\n",
@@ -586,6 +584,57 @@
or_(scratch, Operand(op2));
j(sign, then_label, not_taken);
bind(&ok);
+}
+
+
+void MacroAssembler::TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss) {
+ // Check that the receiver isn't a smi.
+ test(function, Immediate(kSmiTagMask));
+ j(zero, miss, not_taken);
+
+ // Check that the function really is a function.
+ mov(result, FieldOperand(function, HeapObject::kMapOffset));
+ movzx_b(scratch, FieldOperand(result, Map::kInstanceTypeOffset));
+ cmp(scratch, JS_FUNCTION_TYPE);
+ j(not_equal, miss, not_taken);
+
+ // Make sure that the function has an instance prototype.
+ Label non_instance;
+ movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset));
+ test(scratch, Immediate(1 << Map::kHasNonInstancePrototype));
+ j(not_zero, &non_instance, not_taken);
+
+ // Get the prototype or initial map from the function.
+ mov(result,
+ FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // If the prototype or initial map is the hole, don't return it and
+ // simply miss the cache instead. This will allow us to allocate a
+ // prototype object on-demand in the runtime system.
+ cmp(Operand(result), Immediate(Factory::the_hole_value()));
+ j(equal, miss, not_taken);
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ mov(scratch, FieldOperand(result, HeapObject::kMapOffset));
+ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ cmp(scratch, MAP_TYPE);
+ j(not_equal, &done);
+
+ // Get the prototype from the initial map.
+ mov(result, FieldOperand(result, Map::kPrototypeOffset));
+ jmp(&done);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in initial map.
+ bind(&non_instance);
+ mov(result, FieldOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ bind(&done);
}
Modified: branches/bleeding_edge/src/macro-assembler-ia32.h
==============================================================================
--- branches/bleeding_edge/src/macro-assembler-ia32.h (original)
+++ branches/bleeding_edge/src/macro-assembler-ia32.h Wed Oct 8 06:33:16
2008
@@ -179,6 +179,16 @@
void NegativeZeroTest(Register result, Register op1, Register op2,
Register scratch, Label* then_label);
+ // Try to get function prototype of a function and puts the value in
+ // the result register. Checks that the function really is a
+ // function and jumps to the miss label if the fast checks fail. The
+ // function register will be untouched; the other registers may be
+ // clobbered.
+ void TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss);
+
// Generates code for reporting that an illegal operation has
// occurred.
void IllegalOperation(int num_arguments);
Modified: branches/bleeding_edge/src/runtime.js
==============================================================================
--- branches/bleeding_edge/src/runtime.js (original)
+++ branches/bleeding_edge/src/runtime.js Wed Oct 8 06:33:16 2008
@@ -274,7 +274,10 @@
}
-// ECMA-262, section 11.8.6, page 54.
+// ECMA-262, section 11.8.6, page 54. To make the implementation more
+// efficient, the return value should be zero if the 'this' is an
+// instance of F, and non-zero if not. This makes it possible to avoid
+// an expensive ToBoolean conversion in the generated code.
function INSTANCE_OF(F) {
var V = this;
if (!IS_FUNCTION(F)) {
@@ -283,7 +286,7 @@
// If V is not an object, return false.
if (IS_NULL(V) || (!IS_OBJECT(V) && !IS_FUNCTION(V))) {
- return false;
+ return 1;
}
// Get the prototype of F; if it is not an object, throw an error.
@@ -293,7 +296,7 @@
}
// Return whether or not O is in the prototype chain of V.
- return %IsInPrototypeChain(O, V);
+ return %IsInPrototypeChain(O, V) ? 0 : 1;
}
Modified: branches/bleeding_edge/src/stub-cache-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/stub-cache-ia32.cc (original)
+++ branches/bleeding_edge/src/stub-cache-ia32.cc Wed Oct 8 06:33:16 2008
@@ -232,51 +232,9 @@
Register scratch1,
Register scratch2,
Label* miss_label) {
- // Check that the receiver isn't a smi.
- __ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss_label, not_taken);
- // Check that the receiver is a function.
- __ mov(scratch1, FieldOperand(receiver, HeapObject::kMapOffset));
- __ movzx_b(scratch2, FieldOperand(scratch1, Map::kInstanceTypeOffset));
- __ cmp(scratch2, JS_FUNCTION_TYPE);
- __ j(not_equal, miss_label, not_taken);
-
- // Make sure that the function has an instance prototype.
- Label non_instance;
- __ movzx_b(scratch2, FieldOperand(scratch1, Map::kBitFieldOffset));
- __ test(scratch2, Immediate(1 << Map::kHasNonInstancePrototype));
- __ j(not_zero, &non_instance, not_taken);
-
- // Get the prototype or initial map from the function.
- __ mov(scratch1,
- FieldOperand(receiver, JSFunction::kPrototypeOrInitialMapOffset));
-
- // If the prototype or initial map is the hole, don't return it and
- // simply miss the cache instead. This will allow us to allocate a
- // prototype object on-demand in the runtime system.
- __ cmp(Operand(scratch1), Immediate(Factory::the_hole_value()));
- __ j(equal, miss_label, not_taken);
+ __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
__ mov(eax, Operand(scratch1));
-
- // If the function does not have an initial map, we're done.
- Label done;
- __ mov(scratch1, FieldOperand(eax, HeapObject::kMapOffset));
- __ movzx_b(scratch2, FieldOperand(scratch1, Map::kInstanceTypeOffset));
- __ cmp(scratch2, MAP_TYPE);
- __ j(not_equal, &done);
-
- // Get the prototype from the initial map.
- __ mov(eax, FieldOperand(eax, Map::kPrototypeOffset));
-
- // All done: Return the prototype.
- __ bind(&done);
- __ ret(0);
-
- // Non-instance prototype: Fetch prototype from constructor field
- // in initial map.
- __ bind(&non_instance);
- __ mov(eax, FieldOperand(scratch1, Map::kConstructorOffset));
__ ret(0);
}
Modified: branches/bleeding_edge/test/mjsunit/instanceof.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/instanceof.js (original)
+++ branches/bleeding_edge/test/mjsunit/instanceof.js Wed Oct 8 06:33:16
2008
@@ -30,3 +30,58 @@
assertFalse({} instanceof Array);
assertTrue([] instanceof Array);
+
+function TestChains() {
+ var A = {};
+ var B = {};
+ var C = {};
+ B.__proto__ = A;
+ C.__proto__ = B;
+
+ function F() { }
+ F.prototype = A;
+ assertTrue(C instanceof F);
+ assertTrue(B instanceof F);
+ assertFalse(A instanceof F);
+
+ F.prototype = B;
+ assertTrue(C instanceof F);
+ assertFalse(B instanceof F);
+ assertFalse(A instanceof F);
+
+ F.prototype = C;
+ assertFalse(C instanceof F);
+ assertFalse(B instanceof F);
+ assertFalse(A instanceof F);
+}
+
+TestChains();
+
+
+function TestExceptions() {
+ function F() { }
+ var items = [ 1, new Number(42),
+ true,
+ 'string', new String('hest'),
+ {}, [],
+ F, new F(),
+ Object, String ];
+
+ var exceptions = 0;
+ var instanceofs = 0;
+
+ for (var i = 0; i < items.length; i++) {
+ for (var j = 0; j < items.length; j++) {
+ try {
+ if (items[i] instanceof items[j]) instanceofs++;
+ } catch (e) {
+ assertTrue(e instanceof TypeError);
+ exceptions++;
+ }
+ }
+ }
+ assertEquals(10, instanceofs);
+ assertEquals(88, exceptions);
+}
+
+TestExceptions();
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---