Custom call IC for String.fromCharCode.

Review URL: http://codereview.chromium.org/3291015

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5433 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vitalyr@chromium.org 2010-09-09 13:38:01 +00:00
parent d1a674f7c1
commit 624b13a804
9 changed files with 644 additions and 232 deletions

View File

@ -1220,6 +1220,62 @@ void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
}
void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
JSObject* holder,
String* name,
Label* miss) {
ASSERT(holder->IsGlobalObject());
// Get the number of arguments.
const int argc = arguments().immediate();
// Get the receiver from the stack.
__ ldr(r0, MemOperand(sp, argc * kPointerSize));
// If the object is the holder then we know that it's a global
// object which can only happen for contextual calls. In this case,
// the receiver cannot be a smi.
if (object != holder) {
__ tst(r0, Operand(kSmiTagMask));
__ b(eq, miss);
}
// Check that the maps haven't changed.
CheckPrototypes(object, r0, holder, r3, r1, r4, name, miss);
}
void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
JSFunction* function,
Label* miss) {
// Get the value from the cell.
__ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
__ ldr(r1, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
if (Heap::InNewSpace(function)) {
// We can't embed a pointer to a function in new space so we have
// to verify that the shared function info is unchanged. This has
// the nice side effect that multiple closures based on the same
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ tst(r1, Operand(kSmiTagMask));
__ b(eq, miss);
__ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
__ b(ne, miss);
// Check the shared function info. Make sure it hasn't changed.
__ Move(r3, Handle<SharedFunctionInfo>(function->shared()));
__ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ cmp(r4, r3);
__ b(ne, miss);
} else {
__ cmp(r1, Operand(Handle<JSFunction>(function)));
__ b(ne, miss);
}
}
Object* CallStubCompiler::GenerateMissBranch() {
Object* obj = StubCache::ComputeCallMiss(arguments().immediate(), kind_);
if (obj->IsFailure()) return obj;
@ -1266,21 +1322,18 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
Object* CallStubCompiler::CompileArrayPushCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
// -----------------------------------
// If object is not an array, bail out to regular call.
if (!object->IsJSArray()) {
return Heap::undefined_value();
}
// TODO(639): faster implementation.
ASSERT(check == RECEIVER_MAP_CHECK);
// If object is not an array, bail out to regular call.
if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
Label miss;
@ -1313,21 +1366,18 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
Object* CallStubCompiler::CompileArrayPopCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
// -----------------------------------
// If object is not an array, bail out to regular call.
if (!object->IsJSArray()) {
return Heap::undefined_value();
}
// TODO(642): faster implementation.
ASSERT(check == RECEIVER_MAP_CHECK);
// If object is not an array, bail out to regular call.
if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
Label miss;
@ -1358,11 +1408,12 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
}
Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
Object* CallStubCompiler::CompileStringCharCodeAtCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -- r2 : function name
// -- lr : return address
@ -1372,7 +1423,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
if (!object->IsString() || cell != NULL) return Heap::undefined_value();
const int argc = arguments().immediate();
@ -1430,9 +1481,9 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- r2 : function name
// -- lr : return address
@ -1442,7 +1493,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
if (!object->IsString() || cell != NULL) return Heap::undefined_value();
const int argc = arguments().immediate();
@ -1501,6 +1552,80 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
}
Object* CallStubCompiler::CompileStringFromCharCodeCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -- r2 : function name
// -- lr : return address
// -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
// -- ...
// -- sp[argc * 4] : receiver
// -----------------------------------
const int argc = arguments().immediate();
// If the object is not a JSObject or we got an unexpected number of
// arguments, bail out to the regular call.
if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
Label miss;
GenerateNameCheck(name, &miss);
if (cell == NULL) {
__ ldr(r1, MemOperand(sp, 1 * kPointerSize));
STATIC_ASSERT(kSmiTag == 0);
__ tst(r1, Operand(kSmiTagMask));
__ b(eq, &miss);
CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name,
&miss);
} else {
ASSERT(cell->value() == function);
GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
GenerateLoadFunctionFromCell(cell, function, &miss);
}
// Load the char code argument.
Register code = r1;
__ ldr(code, MemOperand(sp, 0 * kPointerSize));
// Check the code is a smi.
Label slow;
STATIC_ASSERT(kSmiTag == 0);
__ tst(code, Operand(kSmiTagMask));
__ b(ne, &slow);
// Convert the smi code to uint16.
__ and_(code, code, Operand(Smi::FromInt(0xffff)));
StringCharFromCodeGenerator char_from_code_generator(code, r0);
char_from_code_generator.GenerateFast(masm());
__ Drop(argc + 1);
__ Ret();
ICRuntimeCallHelper call_helper;
char_from_code_generator.GenerateSlow(masm(), call_helper);
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
__ InvokeFunction(function, arguments(), JUMP_FUNCTION);
__ bind(&miss);
// r2: function name.
Object* obj = GenerateMissBranch();
if (obj->IsFailure()) return obj;
// Return the generated code.
return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
@ -1513,8 +1638,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
const int id = function_info->custom_call_generator_id();
Object* result =
CompileCustomCall(id, object, holder, function, name, check);
Object* result = CompileCustomCall(
id, object, holder, NULL, function, name);
// undefined means bail out to regular compiler.
if (!result->IsUndefined()) {
return result;
@ -1714,6 +1839,16 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// -- r2 : name
// -- lr : return address
// -----------------------------------
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
const int id = function_info->custom_call_generator_id();
Object* result = CompileCustomCall(
id, object, holder, cell, function, name);
// undefined means bail out to regular compiler.
if (!result->IsUndefined()) return result;
}
Label miss;
GenerateNameCheck(name, &miss);
@ -1721,45 +1856,9 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// Get the number of arguments.
const int argc = arguments().immediate();
// Get the receiver from the stack.
__ ldr(r0, MemOperand(sp, argc * kPointerSize));
GenerateGlobalReceiverCheck(object, holder, name, &miss);
// If the object is the holder then we know that it's a global
// object which can only happen for contextual calls. In this case,
// the receiver cannot be a smi.
if (object != holder) {
__ tst(r0, Operand(kSmiTagMask));
__ b(eq, &miss);
}
// Check that the maps haven't changed.
CheckPrototypes(object, r0, holder, r3, r1, r4, name, &miss);
// Get the value from the cell.
__ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
__ ldr(r1, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
if (Heap::InNewSpace(function)) {
// We can't embed a pointer to a function in new space so we have
// to verify that the shared function info is unchanged. This has
// the nice side effect that multiple closures based on the same
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ tst(r1, Operand(kSmiTagMask));
__ b(eq, &miss);
__ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
__ b(ne, &miss);
// Check the shared function info. Make sure it hasn't changed.
__ mov(r3, Operand(Handle<SharedFunctionInfo>(function->shared())));
__ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ cmp(r4, r3);
__ b(ne, &miss);
} else {
__ cmp(r1, Operand(Handle<JSFunction>(function)));
__ b(ne, &miss);
}
GenerateLoadFunctionFromCell(cell, function, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.

View File

@ -1344,23 +1344,33 @@ bool Genesis::InstallNatives() {
}
static void InstallCustomCallGenerator(Handle<JSFunction> holder_function,
const char* function_name,
int id) {
Handle<JSObject> proto(JSObject::cast(holder_function->instance_prototype()));
static void InstallCustomCallGenerator(
Handle<JSFunction> holder_function,
CallStubCompiler::CustomGeneratorOwner owner_flag,
const char* function_name,
int id) {
Handle<JSObject> owner;
if (owner_flag == CallStubCompiler::FUNCTION) {
owner = Handle<JSObject>::cast(holder_function);
} else {
ASSERT(owner_flag == CallStubCompiler::INSTANCE_PROTOTYPE);
owner = Handle<JSObject>(
JSObject::cast(holder_function->instance_prototype()));
}
Handle<String> name = Factory::LookupAsciiSymbol(function_name);
Handle<JSFunction> function(JSFunction::cast(proto->GetProperty(*name)));
Handle<JSFunction> function(JSFunction::cast(owner->GetProperty(*name)));
function->shared()->set_function_data(Smi::FromInt(id));
}
void Genesis::InstallCustomCallGenerators() {
HandleScope scope;
#define INSTALL_CALL_GENERATOR(holder_fun, fun_name, name) \
#define INSTALL_CALL_GENERATOR(holder_fun, owner_flag, fun_name, name) \
{ \
Handle<JSFunction> holder(global_context()->holder_fun##_function()); \
const int id = CallStubCompiler::k##name##CallGenerator; \
InstallCustomCallGenerator(holder, #fun_name, id); \
InstallCustomCallGenerator(holder, CallStubCompiler::owner_flag, \
#fun_name, id); \
}
CUSTOM_CALL_IC_GENERATORS(INSTALL_CALL_GENERATOR)
#undef INSTALL_CALL_GENERATOR

View File

@ -860,9 +860,14 @@ void Assembler::add(const Operand& dst, const Immediate& x) {
void Assembler::and_(Register dst, int32_t imm32) {
and_(dst, Immediate(imm32));
}
void Assembler::and_(Register dst, const Immediate& x) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_arith(4, Operand(dst), Immediate(imm32));
emit_arith(4, Operand(dst), x);
}

View File

@ -577,6 +577,7 @@ class Assembler : public Malloced {
void add(const Operand& dst, const Immediate& x);
void and_(Register dst, int32_t imm32);
void and_(Register dst, const Immediate& x);
void and_(Register dst, const Operand& src);
void and_(const Operand& src, Register dst);
void and_(const Operand& dst, const Immediate& x);

View File

@ -1255,6 +1255,61 @@ void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
}
void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
JSObject* holder,
String* name,
Label* miss) {
ASSERT(holder->IsGlobalObject());
// Get the number of arguments.
const int argc = arguments().immediate();
// Get the receiver from the stack.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
// If the object is the holder then we know that it's a global
// object which can only happen for contextual calls. In this case,
// the receiver cannot be a smi.
if (object != holder) {
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, miss, not_taken);
}
// Check that the maps haven't changed.
CheckPrototypes(object, edx, holder, ebx, eax, edi, name, miss);
}
void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
JSFunction* function,
Label* miss) {
// Get the value from the cell.
__ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell)));
__ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
if (Heap::InNewSpace(function)) {
// We can't embed a pointer to a function in new space so we have
// to verify that the shared function info is unchanged. This has
// the nice side effect that multiple closures based on the same
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ test(edi, Immediate(kSmiTagMask));
__ j(zero, miss, not_taken);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
__ j(not_equal, miss, not_taken);
// Check the shared function info. Make sure it hasn't changed.
__ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
Immediate(Handle<SharedFunctionInfo>(function->shared())));
__ j(not_equal, miss, not_taken);
} else {
__ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
__ j(not_equal, miss, not_taken);
}
}
Object* CallStubCompiler::GenerateMissBranch() {
Object* obj = StubCache::ComputeCallMiss(arguments().immediate(), kind_);
if (obj->IsFailure()) return obj;
@ -1320,9 +1375,9 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
Object* CallStubCompiler::CompileArrayPushCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@ -1330,12 +1385,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
ASSERT(check == RECEIVER_MAP_CHECK);
// If object is not an array, bail out to regular call.
if (!object->IsJSArray()) {
return Heap::undefined_value();
}
if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
Label miss;
@ -1469,9 +1521,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
Object* CallStubCompiler::CompileArrayPopCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@ -1479,12 +1531,9 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
ASSERT(check == RECEIVER_MAP_CHECK);
// If object is not an array, bail out to regular call.
if (!object->IsJSArray()) {
return Heap::undefined_value();
}
if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
Label miss, return_undefined, call_builtin;
@ -1551,11 +1600,12 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
}
Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
Object* CallStubCompiler::CompileStringCharCodeAtCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -- ecx : function name
// -- esp[0] : return address
@ -1565,7 +1615,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
if (!object->IsString() || cell != NULL) return Heap::undefined_value();
const int argc = arguments().immediate();
@ -1621,9 +1671,9 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- ecx : function name
// -- esp[0] : return address
@ -1633,7 +1683,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
if (!object->IsString() || cell != NULL) return Heap::undefined_value();
const int argc = arguments().immediate();
@ -1690,6 +1740,79 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
}
Object* CallStubCompiler::CompileStringFromCharCodeCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -- ecx : function name
// -- esp[0] : return address
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
const int argc = arguments().immediate();
// If the object is not a JSObject or we got an unexpected number of
// arguments, bail out to the regular call.
if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
Label miss;
GenerateNameCheck(name, &miss);
if (cell == NULL) {
__ mov(edx, Operand(esp, 2 * kPointerSize));
STATIC_ASSERT(kSmiTag == 0);
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &miss);
CheckPrototypes(JSObject::cast(object), edx, holder, ebx, eax, edi, name,
&miss);
} else {
ASSERT(cell->value() == function);
GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
GenerateLoadFunctionFromCell(cell, function, &miss);
}
// Load the char code argument.
Register code = ebx;
__ mov(code, Operand(esp, 1 * kPointerSize));
// Check the code is a smi.
Label slow;
STATIC_ASSERT(kSmiTag == 0);
__ test(code, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
// Convert the smi code to uint16.
__ and_(code, Immediate(Smi::FromInt(0xffff)));
StringCharFromCodeGenerator char_from_code_generator(code, eax);
char_from_code_generator.GenerateFast(masm());
__ ret(2 * kPointerSize);
ICRuntimeCallHelper call_helper;
char_from_code_generator.GenerateSlow(masm(), call_helper);
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
__ InvokeFunction(function, arguments(), JUMP_FUNCTION);
__ bind(&miss);
// ecx: function name.
Object* obj = GenerateMissBranch();
if (obj->IsFailure()) return obj;
// Return the generated code.
return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
@ -1706,12 +1829,10 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
const int id = function_info->custom_call_generator_id();
Object* result =
CompileCustomCall(id, object, holder, function, name, check);
Object* result = CompileCustomCall(
id, object, holder, NULL, function, name);
// undefined means bail out to regular compiler.
if (!result->IsUndefined()) {
return result;
}
if (!result->IsUndefined()) return result;
}
Label miss_in_smi_check;
@ -1922,6 +2043,16 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
const int id = function_info->custom_call_generator_id();
Object* result = CompileCustomCall(
id, object, holder, cell, function, name);
// undefined means bail out to regular compiler.
if (!result->IsUndefined()) return result;
}
Label miss;
GenerateNameCheck(name, &miss);
@ -1929,44 +2060,9 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// Get the number of arguments.
const int argc = arguments().immediate();
// Get the receiver from the stack.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
GenerateGlobalReceiverCheck(object, holder, name, &miss);
// If the object is the holder then we know that it's a global
// object which can only happen for contextual calls. In this case,
// the receiver cannot be a smi.
if (object != holder) {
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken);
}
// Check that the maps haven't changed.
CheckPrototypes(object, edx, holder, ebx, eax, edi, name, &miss);
// Get the value from the cell.
__ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell)));
__ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
if (Heap::InNewSpace(function)) {
// We can't embed a pointer to a function in new space so we have
// to verify that the shared function info is unchanged. This has
// the nice side effect that multiple closures based on the same
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ test(edi, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
__ j(not_equal, &miss, not_taken);
// Check the shared function info. Make sure it hasn't changed.
__ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
Immediate(Handle<SharedFunctionInfo>(function->shared())));
__ j(not_equal, &miss, not_taken);
} else {
__ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
__ j(not_equal, &miss, not_taken);
}
GenerateLoadFunctionFromCell(cell, function, &miss);
// Patch the receiver on the stack with the global proxy.
if (object->IsGlobalObject()) {

View File

@ -1222,23 +1222,23 @@ CallStubCompiler::CallStubCompiler(int argc,
Object* CallStubCompiler::CompileCustomCall(int generator_id,
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* fname,
CheckType check) {
ASSERT(generator_id >= 0 && generator_id < kNumCallGenerators);
switch (generator_id) {
#define CALL_GENERATOR_CASE(ignored1, ignored2, name) \
case k##name##CallGenerator: \
return CallStubCompiler::Compile##name##Call(object, \
holder, \
function, \
fname, \
check);
CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE)
String* fname) {
ASSERT(generator_id >= 0 && generator_id < kNumCallGenerators);
switch (generator_id) {
#define CALL_GENERATOR_CASE(ignored1, ignored2, ignored3, name) \
case k##name##CallGenerator: \
return CallStubCompiler::Compile##name##Call(object, \
holder, \
cell, \
function, \
fname);
CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE)
#undef CALL_GENERATOR_CASE
}
UNREACHABLE();
return Heap::undefined_value();
}
UNREACHABLE();
return Heap::undefined_value();
}

View File

@ -612,21 +612,29 @@ class KeyedStoreStubCompiler: public StubCompiler {
// Installation of custom call generators for the selected builtins is
// handled by the bootstrapper.
//
// Each entry has a name of a global function (lowercased), a name of
// a builtin function on its instance prototype (the one the generator
// is set for), and a name of a generator itself (used to build ids
// and generator function names).
#define CUSTOM_CALL_IC_GENERATORS(V) \
V(array, push, ArrayPush) \
V(array, pop, ArrayPop) \
V(string, charCodeAt, StringCharCodeAt) \
V(string, charAt, StringCharAt)
// Each entry has a name of a global function (lowercased), a flag
// controlling whether the generator is set on the function itself or
// on its instance prototype, a name of a builtin function on the
// function or its instance prototype (the one the generator is set
// for), and a name of a generator itself (used to build ids and
// generator function names).
#define CUSTOM_CALL_IC_GENERATORS(V) \
V(array, INSTANCE_PROTOTYPE, push, ArrayPush) \
V(array, INSTANCE_PROTOTYPE, pop, ArrayPop) \
V(string, INSTANCE_PROTOTYPE, charCodeAt, StringCharCodeAt) \
V(string, INSTANCE_PROTOTYPE, charAt, StringCharAt) \
V(string, FUNCTION, fromCharCode, StringFromCharCode)
class CallStubCompiler: public StubCompiler {
public:
enum CustomGeneratorOwner {
FUNCTION,
INSTANCE_PROTOTYPE
};
enum {
#define DECLARE_CALL_GENERATOR_ID(ignored1, ignored2, name) \
#define DECLARE_CALL_GENERATOR_ID(ignored1, ignore2, ignored3, name) \
k##name##CallGenerator,
CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR_ID)
#undef DECLARE_CALL_GENERATOR_ID
@ -656,20 +664,21 @@ class CallStubCompiler: public StubCompiler {
JSFunction* function,
String* name);
// Compiles a custom call constant IC using the generator with given id.
// Compiles a custom call constant/global IC using the generator
// with given id. For constant calls cell is NULL.
Object* CompileCustomCall(int generator_id,
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check);
String* name);
#define DECLARE_CALL_GENERATOR(ignored1, ignored2, name) \
Object* Compile##name##Call(Object* object, \
JSObject* holder, \
JSFunction* function, \
String* fname, \
CheckType check);
#define DECLARE_CALL_GENERATOR(ignored1, ignored2, ignored3, name) \
Object* Compile##name##Call(Object* object, \
JSObject* holder, \
JSGlobalPropertyCell* cell, \
JSFunction* function, \
String* fname);
CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR)
#undef DECLARE_CALL_GENERATOR
@ -689,6 +698,17 @@ class CallStubCompiler: public StubCompiler {
void GenerateNameCheck(String* name, Label* miss);
void GenerateGlobalReceiverCheck(JSObject* object,
JSObject* holder,
String* name,
Label* miss);
// Generates code to load the function from the cell checking that
// it still contains the same function.
void GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
JSFunction* function,
Label* miss);
// Generates a jump to CallIC miss stub. Returns Failure if the jump cannot
// be generated.
Object* GenerateMissBranch();

View File

@ -821,6 +821,59 @@ void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
}
void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
JSObject* holder,
String* name,
Label* miss) {
ASSERT(holder->IsGlobalObject());
// Get the number of arguments.
const int argc = arguments().immediate();
// Get the receiver from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
// If the object is the holder then we know that it's a global
// object which can only happen for contextual calls. In this case,
// the receiver cannot be a smi.
if (object != holder) {
__ JumpIfSmi(rdx, miss);
}
// Check that the maps haven't changed.
CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, miss);
}
void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
JSFunction* function,
Label* miss) {
// Get the value from the cell.
__ Move(rdi, Handle<JSGlobalPropertyCell>(cell));
__ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
if (Heap::InNewSpace(function)) {
// We can't embed a pointer to a function in new space so we have
// to verify that the shared function info is unchanged. This has
// the nice side effect that multiple closures based on the same
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ JumpIfSmi(rdi, miss);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
__ j(not_equal, miss);
// Check the shared function info. Make sure it hasn't changed.
__ Move(rax, Handle<SharedFunctionInfo>(function->shared()));
__ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax);
__ j(not_equal, miss);
} else {
__ Cmp(rdi, Handle<JSFunction>(function));
__ j(not_equal, miss);
}
}
Object* CallStubCompiler::GenerateMissBranch() {
Object* obj = StubCache::ComputeCallMiss(arguments().immediate(), kind_);
if (obj->IsFailure()) return obj;
@ -847,12 +900,10 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
const int id = function_info->custom_call_generator_id();
Object* result =
CompileCustomCall(id, object, holder, function, name, check);
Object* result = CompileCustomCall(
id, object, holder, NULL, function, name);
// undefined means bail out to regular compiler.
if (!result->IsUndefined()) {
return result;
}
if (!result->IsUndefined()) return result;
}
Label miss_in_smi_check;
@ -1043,9 +1094,9 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
Object* CallStubCompiler::CompileArrayPushCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- rcx : name
// -- rsp[0] : return address
@ -1053,12 +1104,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
// -- ...
// -- rsp[(argc + 1) * 8] : receiver
// -----------------------------------
ASSERT(check == RECEIVER_MAP_CHECK);
// If object is not an array, bail out to regular call.
if (!object->IsJSArray()) {
return Heap::undefined_value();
}
if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
Label miss;
@ -1204,9 +1252,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
Object* CallStubCompiler::CompileArrayPopCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- rcx : name
// -- rsp[0] : return address
@ -1214,12 +1262,9 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
// -- ...
// -- rsp[(argc + 1) * 8] : receiver
// -----------------------------------
ASSERT(check == RECEIVER_MAP_CHECK);
// If object is not an array, bail out to regular call.
if (!object->IsJSArray()) {
return Heap::undefined_value();
}
if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
Label miss, return_undefined, call_builtin;
@ -1289,9 +1334,9 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name,
CheckType check) {
String* name) {
// ----------- S t a t e -------------
// -- rcx : function name
// -- rsp[0] : return address
@ -1301,7 +1346,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
if (!object->IsString() || cell != NULL) return Heap::undefined_value();
const int argc = arguments().immediate();
@ -1358,11 +1403,12 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
}
Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
Object* CallStubCompiler::CompileStringCharCodeAtCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -- rcx : function name
// -- rsp[0] : return address
@ -1372,7 +1418,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
if (!object->IsString() || cell != NULL) return Heap::undefined_value();
const int argc = arguments().immediate();
@ -1426,6 +1472,75 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
}
Object* CallStubCompiler::CompileStringFromCharCodeCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -- rcx : function name
// -- rsp[0] : return address
// -- rsp[(argc - n) * 8] : arg[n] (zero-based)
// -- ...
// -- rsp[(argc + 1) * 8] : receiver
// -----------------------------------
const int argc = arguments().immediate();
// If the object is not a JSObject or we got an unexpected number of
// arguments, bail out to the regular call.
if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
Label miss;
GenerateNameCheck(name, &miss);
if (cell == NULL) {
__ movq(rdx, Operand(rsp, 2 * kPointerSize));
__ JumpIfSmi(rdx, &miss);
CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name,
&miss);
} else {
ASSERT(cell->value() == function);
GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
GenerateLoadFunctionFromCell(cell, function, &miss);
}
// Load the char code argument.
Register code = rbx;
__ movq(code, Operand(rsp, 1 * kPointerSize));
// Check the code is a smi.
Label slow;
__ JumpIfNotSmi(code, &slow);
// Convert the smi code to uint16.
__ SmiAndConstant(code, code, Smi::FromInt(0xffff));
StringCharFromCodeGenerator char_from_code_generator(code, rax);
char_from_code_generator.GenerateFast(masm());
__ ret(2 * kPointerSize);
ICRuntimeCallHelper call_helper;
char_from_code_generator.GenerateSlow(masm(), call_helper);
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
__ InvokeFunction(function, arguments(), JUMP_FUNCTION);
__ bind(&miss);
// rcx: function name.
Object* obj = GenerateMissBranch();
if (obj->IsFailure()) return obj;
// Return the generated code.
return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
}
Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
@ -1498,7 +1613,6 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
// -----------------------------------
// rcx : function name
// rsp[0] : return address
// rsp[8] : argument argc
@ -1506,6 +1620,17 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// ...
// rsp[argc * 8] : argument 1
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
const int id = function_info->custom_call_generator_id();
Object* result = CompileCustomCall(
id, object, holder, cell, function, name);
// undefined means bail out to regular compiler.
if (!result->IsUndefined()) return result;
}
Label miss;
GenerateNameCheck(name, &miss);
@ -1513,42 +1638,9 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// Get the number of arguments.
const int argc = arguments().immediate();
// Get the receiver from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
GenerateGlobalReceiverCheck(object, holder, name, &miss);
// If the object is the holder then we know that it's a global
// object which can only happen for contextual calls. In this case,
// the receiver cannot be a smi.
if (object != holder) {
__ JumpIfSmi(rdx, &miss);
}
// Check that the maps haven't changed.
CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, &miss);
// Get the value from the cell.
__ Move(rdi, Handle<JSGlobalPropertyCell>(cell));
__ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
if (Heap::InNewSpace(function)) {
// We can't embed a pointer to a function in new space so we have
// to verify that the shared function info is unchanged. This has
// the nice side effect that multiple closures based on the same
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ JumpIfSmi(rdi, &miss);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
__ j(not_equal, &miss);
// Check the shared function info. Make sure it hasn't changed.
__ Move(rax, Handle<SharedFunctionInfo>(function->shared()));
__ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax);
__ j(not_equal, &miss);
} else {
__ Cmp(rdi, Handle<JSFunction>(function));
__ j(not_equal, &miss);
}
GenerateLoadFunctionFromCell(cell, function, &miss);
// Patch the receiver on the stack with the global proxy.
if (object->IsGlobalObject()) {

View File

@ -0,0 +1,89 @@
// Copyright 2010 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 String.fromCharCode.
// Test various receivers and arguments passed to String.fromCharCode.
Object.prototype.fromCharCode = function(x) { return this; };
var fcc = String.fromCharCode;
var fcc2 = fcc;
function constFun(x) { return function(y) { return x; }; }
function test(num) {
assertEquals(" ", String.fromCharCode(0x20));
assertEquals(" ", String.fromCharCode(0x20 + 0x10000));
assertEquals(" ", String.fromCharCode(0x20 - 0x10000));
assertEquals(" ", String.fromCharCode(0x20 + 0.5));
assertEquals("\u1234", String.fromCharCode(0x1234));
assertEquals("\u1234", String.fromCharCode(0x1234 + 0x10000));
assertEquals("\u1234", String.fromCharCode(0x1234 - 0x10000));
assertEquals("\u1234", String.fromCharCode(0x1234 + 0.5));
assertEquals(" ", String.fromCharCode(0x20, 0x20));
assertEquals(" ", String.fromCharCode(0x20 + 0.5, 0x20));
assertEquals(" ", fcc(0x20));
assertEquals(" ", fcc(0x20 + 0x10000));
assertEquals(" ", fcc(0x20 - 0x10000));
assertEquals(" ", fcc(0x20 + 0.5));
assertEquals("\u1234", fcc(0x1234));
assertEquals("\u1234", fcc(0x1234 + 0x10000));
assertEquals("\u1234", fcc(0x1234 - 0x10000));
assertEquals("\u1234", fcc(0x1234 + 0.5));
assertEquals(" ", fcc(0x20, 0x20));
assertEquals(" ", fcc(0x20 + 0.5, 0x20));
var receiver = (num < 5) ? String : (num < 9) ? "dummy" : 42;
fcc2 = (num < 5) ? fcc : (num < 9) ? constFun("dummy") : constFun(42);
var expected = (num < 5) ? " " : (num < 9) ? "dummy" : 42;
assertEquals(expected, receiver.fromCharCode(0x20));
assertEquals(expected, receiver.fromCharCode(0x20 - 0x10000));
assertEquals(expected, receiver.fromCharCode(0x20 + 0.5));
assertEquals(expected, fcc2(0x20));
assertEquals(expected, fcc2(0x20 - 0x10000));
assertEquals(expected, fcc2(0x20 + 0.5));
}
// Use loop to test the custom IC.
for (var i = 0; i < 10; i++) {
test(i);
}
// Test the custom IC works correctly when the map changes.
for (var i = 0; i < 10; i++) {
var expected = (i < 5) ? " " : 42;
if (i == 5) String.fromCharCode = function() { return 42; };
assertEquals(expected, String.fromCharCode(0x20));
}