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:
parent
d1a674f7c1
commit
624b13a804
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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()) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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()) {
|
||||
|
89
test/mjsunit/string-fromcharcode.js
Normal file
89
test/mjsunit/string-fromcharcode.js
Normal 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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user