Improve the generated code for the instanceof operator,

and extended the instanceof test case.
Review URL: http://codereview.chromium.org/6341

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@470 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kasperl@chromium.org 2008-10-08 13:33:16 +00:00
parent 614d78367f
commit 77643dbef6
9 changed files with 192 additions and 78 deletions

View File

@ -110,6 +110,8 @@ const char* CodeStub::MajorName(CodeStub::Major major_key) {
return "RevertToNumber";
case ToBoolean:
return "ToBoolean";
case Instanceof:
return "Instanceof";
case CounterOp:
return "CounterOp";
case ArgumentsAccess:

View File

@ -44,6 +44,7 @@ class CodeStub BASE_EMBEDDED {
UnarySub,
RevertToNumber,
ToBoolean,
Instanceof,
CounterOp,
ArgumentsAccess,
Runtime,
@ -82,7 +83,7 @@ class CodeStub BASE_EMBEDDED {
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()); }

View File

@ -887,8 +887,6 @@ class GetPropertyStub : public CodeStub {
Major MajorKey() { return GetProperty; }
int MinorKey() { return 0; }
void Generate(MacroAssembler* masm);
const char* GetName() { return "GetPropertyStub"; }
};
@ -900,8 +898,6 @@ class SetPropertyStub : public CodeStub {
Major MajorKey() { return SetProperty; }
int MinorKey() { return 0; }
void Generate(MacroAssembler* masm);
const char* GetName() { return "GetPropertyStub"; }
};
@ -951,8 +947,6 @@ class InvokeBuiltinStub : public CodeStub {
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 @@ class CallFunctionStub: public CodeStub {
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 @@ void ArmCodeGenerator::VisitCompareOperation(CompareOperation* node) {
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:

View File

@ -894,18 +894,8 @@ class ToBooleanStub: public CodeStub {
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 @@ class CompareStub: public CodeStub {
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 @@ class CallFunctionStub: public CodeStub {
private:
int argc_;
const char* GetName() { return "CallFunctionStub"; }
#ifdef DEBUG
void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
#endif
@ -3521,8 +3507,6 @@ class RevertToNumberStub: public CodeStub {
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 @@ class CounterOpStub: public CodeStub {
}
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 @@ void Ia32CodeGenerator::VisitThisFunction(ThisFunction* node) {
}
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 @@ void Ia32CodeGenerator::VisitCompareOperation(CompareOperation* node) {
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:
@ -5289,6 +5285,53 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
}
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);
}
#undef __
// -----------------------------------------------------------------------------

View File

@ -99,8 +99,6 @@ class RecordWriteStub : public CodeStub {
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",
@ -589,6 +587,57 @@ void MacroAssembler::NegativeZeroTest(Register result,
}
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);
}
void MacroAssembler::CallStub(CodeStub* stub) {
ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
call(stub->GetCode(), RelocInfo::CODE_TARGET);

View File

@ -179,6 +179,16 @@ class MacroAssembler: public Assembler {
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);

View File

@ -274,7 +274,10 @@ function IN(x) {
}
// 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 @@ function INSTANCE_OF(F) {
// 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 @@ function INSTANCE_OF(F) {
}
// Return whether or not O is in the prototype chain of V.
return %IsInPrototypeChain(O, V);
return %IsInPrototypeChain(O, V) ? 0 : 1;
}

View File

@ -232,51 +232,9 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
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);
}

View File

@ -30,3 +30,58 @@ assertTrue([] instanceof Object);
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();