From: vitalyr@chromium.org Date: Thu, 9 Sep 2010 13:38:01 +0000 (+0000) Subject: Custom call IC for String.fromCharCode. X-Git-Tag: upstream/4.7.83~21225 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=624b13a8040fee9ce1dab8997e65ab59d82d4fa8;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 344cb6f..0da5f64 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -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(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(function->shared())); + __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ cmp(r4, r3); + __ b(ne, miss); + } else { + __ cmp(r1, Operand(Handle(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)); - - // 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(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); + GenerateGlobalReceiverCheck(object, holder, name, &miss); - // Check the shared function info. Make sure it hasn't changed. - __ mov(r3, Operand(Handle(function->shared()))); - __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ cmp(r4, r3); - __ b(ne, &miss); - } else { - __ cmp(r1, Operand(Handle(function))); - __ b(ne, &miss); - } + GenerateLoadFunctionFromCell(cell, function, &miss); // Patch the receiver on the stack with the global proxy if // necessary. diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index a82d1d6..6e6c2c6 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1344,23 +1344,33 @@ bool Genesis::InstallNatives() { } -static void InstallCustomCallGenerator(Handle holder_function, - const char* function_name, - int id) { - Handle proto(JSObject::cast(holder_function->instance_prototype())); +static void InstallCustomCallGenerator( + Handle holder_function, + CallStubCompiler::CustomGeneratorOwner owner_flag, + const char* function_name, + int id) { + Handle owner; + if (owner_flag == CallStubCompiler::FUNCTION) { + owner = Handle::cast(holder_function); + } else { + ASSERT(owner_flag == CallStubCompiler::INSTANCE_PROTOTYPE); + owner = Handle( + JSObject::cast(holder_function->instance_prototype())); + } Handle name = Factory::LookupAsciiSymbol(function_name); - Handle function(JSFunction::cast(proto->GetProperty(*name))); + Handle 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 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 diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 2565acb..eef307d 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -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); } diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 8a5a4c5..928f172 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -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); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 7fc3f81..828e71a 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -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(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(function->shared()))); + __ j(not_equal, miss, not_taken); + } else { + __ cmp(Operand(edi), Immediate(Handle(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(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(function->shared()))); - __ j(not_equal, &miss, not_taken); - } else { - __ cmp(Operand(edi), Immediate(Handle(function))); - __ j(not_equal, &miss, not_taken); - } + GenerateLoadFunctionFromCell(cell, function, &miss); // Patch the receiver on the stack with the global proxy. if (object->IsGlobalObject()) { diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 7a490d3..34989d3 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -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(); } diff --git a/src/stub-cache.h b/src/stub-cache.h index bf14a4f..388bb52 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -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(); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index f500ce6..765a90c 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -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(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(function->shared())); + __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax); + __ j(not_equal, miss); + } else { + __ Cmp(rdi, Handle(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)); - - // 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(cell)); - __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset)); + GenerateGlobalReceiverCheck(object, holder, name, &miss); - // 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(function->shared())); - __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax); - __ j(not_equal, &miss); - } else { - __ Cmp(rdi, Handle(function)); - __ j(not_equal, &miss); - } + GenerateLoadFunctionFromCell(cell, function, &miss); // Patch the receiver on the stack with the global proxy. if (object->IsGlobalObject()) { diff --git a/test/mjsunit/string-fromcharcode.js b/test/mjsunit/string-fromcharcode.js new file mode 100644 index 0000000..7a2db5f --- /dev/null +++ b/test/mjsunit/string-fromcharcode.js @@ -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)); +}