First take on custom call generators.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4108 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
antonm@chromium.org 2010-03-11 16:24:31 +00:00
parent cb1f817f77
commit de070ccfa6
22 changed files with 551 additions and 238 deletions

View File

@ -815,6 +815,55 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
}
Object* CallStubCompiler::CompileArrayPushCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
// -----------------------------------
// TODO(639): faster implementation.
ASSERT(check == RECEIVER_MAP_CHECK);
Label miss;
// Get the receiver from the stack
const int argc = arguments().immediate();
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
// Check that the receiver isn't a smi.
__ tst(r1, Operand(kSmiTagMask));
__ b(eq, &miss);
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
if (object->IsGlobalObject()) {
__ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
__ str(r3, MemOperand(sp, argc * kPointerSize));
}
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
argc + 1,
1);
// Handle call cache miss.
__ bind(&miss);
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
__ Jump(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
String* function_name = NULL;
if (function->shared()->name()->IsString()) {
function_name = String::cast(function->shared()->name());
}
return GetCode(CONSTANT_FUNCTION, function_name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
@ -824,6 +873,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// -- r2 : name
// -- lr : return address
// -----------------------------------
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
CustomCallGenerator generator =
ToCData<CustomCallGenerator>(function_info->function_data());
return generator(this, object, holder, function, name, check);
}
Label miss;
// Get the receiver from the stack

View File

@ -1088,15 +1088,6 @@ function ArrayIsArray(obj) {
return IS_ARRAY(obj);
}
// -------------------------------------------------------------------
function UpdateFunctionLengths(lengths) {
for (var key in lengths) {
%FunctionSetLength(this[key], lengths[key]);
}
}
// -------------------------------------------------------------------
function SetupArray() {
@ -1109,47 +1100,47 @@ function SetupArray() {
"isArray", ArrayIsArray
));
var specialFunctions = %SpecialArrayFunctions({});
function getFunction(name, jsBuiltin, len) {
var f = jsBuiltin;
if (specialFunctions.hasOwnProperty(name)) {
f = specialFunctions[name];
}
if (arguments.length == 3) {
%FunctionSetLength(f, len);
}
return f;
}
// Setup non-enumerable functions of the Array.prototype object and
// set their names.
InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
"toString", ArrayToString,
"toLocaleString", ArrayToLocaleString,
"join", ArrayJoin,
"pop", ArrayPop,
"push", ArrayPush,
"concat", ArrayConcat,
"reverse", ArrayReverse,
"shift", ArrayShift,
"unshift", ArrayUnshift,
"slice", ArraySlice,
"splice", ArraySplice,
"sort", ArraySort,
"filter", ArrayFilter,
"forEach", ArrayForEach,
"some", ArraySome,
"every", ArrayEvery,
"map", ArrayMap,
"indexOf", ArrayIndexOf,
"lastIndexOf", ArrayLastIndexOf,
"reduce", ArrayReduce,
"reduceRight", ArrayReduceRight
));
// Manipulate the length of some of the functions to meet
// expectations set by ECMA-262 or Mozilla.
UpdateFunctionLengths({
ArrayFilter: 1,
ArrayForEach: 1,
ArraySome: 1,
ArrayEvery: 1,
ArrayMap: 1,
ArrayIndexOf: 1,
ArrayLastIndexOf: 1,
ArrayPush: 1,
ArrayReduce: 1,
ArrayReduceRight: 1
});
InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
"toString", getFunction("toString", ArrayToString),
"toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
"join", getFunction("join", ArrayJoin),
"pop", getFunction("pop", ArrayPop),
"push", getFunction("push", ArrayPush, 1),
"concat", getFunction("concat", ArrayConcat),
"reverse", getFunction("reverse", ArrayReverse),
"shift", getFunction("shift", ArrayShift),
"unshift", getFunction("unshift", ArrayUnshift, 1),
"slice", getFunction("slice", ArraySlice, 2),
"splice", getFunction("splice", ArraySplice, 2),
"sort", getFunction("sort", ArraySort),
"filter", getFunction("filter", ArrayFilter, 1),
"forEach", getFunction("forEach", ArrayForEach, 1),
"some", getFunction("some", ArraySome, 1),
"every", getFunction("every", ArrayEvery, 1),
"map", getFunction("map", ArrayMap, 1),
"indexOf", getFunction("indexOf", ArrayIndexOf, 1),
"lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
"reduce", getFunction("reduce", ArrayReduce, 1),
"reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
));
%FinishArrayPrototypeSetup($Array.prototype);
}

View File

@ -245,15 +245,6 @@ class Genesis BASE_EMBEDDED {
bool make_prototype_enumerable = false);
void MakeFunctionInstancePrototypeWritable();
Handle<JSFunction> MakeFunctionForBuiltin(Handle<String> name,
Handle<Code> code);
void OverrideWithSpecialFunction(Handle<JSObject> prototype,
const char* name,
Handle<Code> code);
void InstallSpecialFunctions();
static bool CompileBuiltin(int index);
static bool CompileNative(Vector<const char> name, Handle<String> source);
static bool CompileScriptCached(Vector<const char> name,
@ -1458,73 +1449,6 @@ void Genesis::MakeFunctionInstancePrototypeWritable() {
}
Handle<JSFunction> Genesis::MakeFunctionForBuiltin(Handle<String> name,
Handle<Code> code) {
Handle<JSFunction> optimized = Factory::NewFunction(name,
JS_OBJECT_TYPE,
JSObject::kHeaderSize,
code,
false);
optimized->shared()->DontAdaptArguments();
return optimized;
}
void Genesis::OverrideWithSpecialFunction(Handle<JSObject> prototype,
const char* name,
Handle<Code> code) {
Handle<String> key = Factory::LookupAsciiSymbol(name);
Handle<Object> old_value = GetProperty(prototype, key);
// Check if the function is present in the first place.
// For example, FLAG_natives_file could affect if Array functions
// are installed at all.
if (!old_value->IsJSFunction()) return;
int old_length = Handle<JSFunction>::cast(old_value)->shared()->length();
Handle<JSFunction> optimized = MakeFunctionForBuiltin(key, code);
optimized->shared()->set_length(old_length);
SetProperty(prototype, key, optimized, NONE);
}
void Genesis::InstallSpecialFunctions() {
HandleScope scope;
Handle<JSObject> global = Handle<JSObject>(global_context()->global());
// Add special versions for some Array.prototype functions.
Handle<JSFunction> function =
Handle<JSFunction>(
JSFunction::cast(global->GetProperty(Heap::Array_symbol())));
Handle<JSObject> visible_prototype =
Handle<JSObject>(JSObject::cast(function->prototype()));
// Remember to put those specializations on the hidden prototype if present.
Handle<JSObject> special_prototype;
Handle<Object> superproto(visible_prototype->GetPrototype());
if (superproto->IsJSObject() &&
JSObject::cast(*superproto)->map()->is_hidden_prototype()) {
special_prototype = Handle<JSObject>::cast(superproto);
} else {
special_prototype = visible_prototype;
}
OverrideWithSpecialFunction(
special_prototype, "pop",
Handle<Code>(Builtins::builtin(Builtins::ArrayPop)));
OverrideWithSpecialFunction(
special_prototype, "push",
Handle<Code>(Builtins::builtin(Builtins::ArrayPush)));
OverrideWithSpecialFunction(
special_prototype, "shift",
Handle<Code>(Builtins::builtin(Builtins::ArrayShift)));
OverrideWithSpecialFunction(
special_prototype, "unshift",
Handle<Code>(Builtins::builtin(Builtins::ArrayUnshift)));
OverrideWithSpecialFunction(
special_prototype, "slice",
Handle<Code>(Builtins::builtin(Builtins::ArraySlice)));
OverrideWithSpecialFunction(
special_prototype, "splice",
Handle<Code>(Builtins::builtin(Builtins::ArraySplice)));
}
Genesis::Genesis(Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions) {
@ -1548,7 +1472,6 @@ Genesis::Genesis(Handle<Object> global_object,
if (!InstallNatives()) return;
MakeFunctionInstancePrototypeWritable();
InstallSpecialFunctions();
if (!ConfigureGlobalObjects(global_template)) return;

View File

@ -790,20 +790,19 @@ static Object* HandleApiCallHelper(
HandleScope scope;
Handle<JSFunction> function = args.called_function();
ASSERT(function->shared()->IsApiFunction());
FunctionTemplateInfo* fun_data = function->shared()->get_api_func_data();
if (is_construct) {
Handle<FunctionTemplateInfo> desc =
Handle<FunctionTemplateInfo>(
FunctionTemplateInfo::cast(function->shared()->function_data()));
Handle<FunctionTemplateInfo> desc(fun_data);
bool pending_exception = false;
Factory::ConfigureInstance(desc, Handle<JSObject>::cast(args.receiver()),
&pending_exception);
ASSERT(Top::has_pending_exception() == pending_exception);
if (pending_exception) return Failure::Exception();
fun_data = *desc;
}
FunctionTemplateInfo* fun_data =
FunctionTemplateInfo::cast(function->shared()->function_data());
Object* raw_holder = TypeCheck(args.length(), &args[0], fun_data);
if (raw_holder->IsNull()) {
@ -874,8 +873,8 @@ BUILTIN(HandleApiCallConstruct) {
static void VerifyTypeCheck(Handle<JSObject> object,
Handle<JSFunction> function) {
FunctionTemplateInfo* info =
FunctionTemplateInfo::cast(function->shared()->function_data());
ASSERT(function->shared()->IsApiFunction());
FunctionTemplateInfo* info = function->shared()->get_api_func_data();
if (info->signature()->IsUndefined()) return;
SignatureInfo* signature = SignatureInfo::cast(info->signature());
Object* receiver_type = signature->receiver();
@ -959,9 +958,9 @@ static Object* HandleApiCallAsFunctionOrConstructor(
// used to create the called object.
ASSERT(obj->map()->has_instance_call_handler());
JSFunction* constructor = JSFunction::cast(obj->map()->constructor());
Object* template_info = constructor->shared()->function_data();
ASSERT(constructor->shared()->IsApiFunction());
Object* handler =
FunctionTemplateInfo::cast(template_info)->instance_call_handler();
constructor->shared()->get_api_func_data()->instance_call_handler();
ASSERT(!handler->IsUndefined());
CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
Object* callback_obj = call_data->callback();

View File

@ -866,6 +866,7 @@ Handle<JSFunction> Factory::CreateApiFunction(
map->set_instance_descriptors(*array);
}
ASSERT(result->shared()->IsApiFunction());
return result;
}

View File

@ -10217,6 +10217,12 @@ void NumberToStringStub::Generate(MacroAssembler* masm) {
}
void RecordWriteStub::Generate(MacroAssembler* masm) {
masm->RecordWriteHelper(object_, addr_, scratch_);
masm->ret(0);
}
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;

View File

@ -977,6 +977,42 @@ class NumberToStringStub: public CodeStub {
};
class RecordWriteStub : public CodeStub {
public:
RecordWriteStub(Register object, Register addr, Register scratch)
: object_(object), addr_(addr), scratch_(scratch) { }
void Generate(MacroAssembler* masm);
private:
Register object_;
Register addr_;
Register scratch_;
#ifdef DEBUG
void Print() {
PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
object_.code(), addr_.code(), scratch_.code());
}
#endif
// Minor key encoding in 12 bits of three registers (object, address and
// scratch) OOOOAAAASSSS.
class ScratchBits: public BitField<uint32_t, 0, 4> {};
class AddressBits: public BitField<uint32_t, 4, 4> {};
class ObjectBits: public BitField<uint32_t, 8, 4> {};
Major MajorKey() { return RecordWrite; }
int MinorKey() {
// Encode the registers.
return ObjectBits::encode(object_.code()) |
AddressBits::encode(addr_.code()) |
ScratchBits::encode(scratch_.code());
}
};
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_

View File

@ -47,33 +47,32 @@ MacroAssembler::MacroAssembler(void* buffer, int size)
}
static void RecordWriteHelper(MacroAssembler* masm,
Register object,
Register addr,
Register scratch) {
void MacroAssembler::RecordWriteHelper(Register object,
Register addr,
Register scratch) {
Label fast;
// Compute the page start address from the heap object pointer, and reuse
// the 'object' register for it.
masm->and_(object, ~Page::kPageAlignmentMask);
and_(object, ~Page::kPageAlignmentMask);
Register page_start = object;
// Compute the bit addr in the remembered set/index of the pointer in the
// page. Reuse 'addr' as pointer_offset.
masm->sub(addr, Operand(page_start));
masm->shr(addr, kObjectAlignmentBits);
sub(addr, Operand(page_start));
shr(addr, kObjectAlignmentBits);
Register pointer_offset = addr;
// If the bit offset lies beyond the normal remembered set range, it is in
// the extra remembered set area of a large object.
masm->cmp(pointer_offset, Page::kPageSize / kPointerSize);
masm->j(less, &fast);
cmp(pointer_offset, Page::kPageSize / kPointerSize);
j(less, &fast);
// Adjust 'page_start' so that addressing using 'pointer_offset' hits the
// extra remembered set after the large object.
// Find the length of the large object (FixedArray).
masm->mov(scratch, Operand(page_start, Page::kObjectStartOffset
mov(scratch, Operand(page_start, Page::kObjectStartOffset
+ FixedArray::kLengthOffset));
Register array_length = scratch;
@ -83,59 +82,40 @@ static void RecordWriteHelper(MacroAssembler* masm,
// Add the delta between the end of the normal RSet and the start of the
// extra RSet to 'page_start', so that addressing the bit using
// 'pointer_offset' hits the extra RSet words.
masm->lea(page_start,
Operand(page_start, array_length, times_pointer_size,
Page::kObjectStartOffset + FixedArray::kHeaderSize
- Page::kRSetEndOffset));
lea(page_start,
Operand(page_start, array_length, times_pointer_size,
Page::kObjectStartOffset + FixedArray::kHeaderSize
- Page::kRSetEndOffset));
// NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
// to limit code size. We should probably evaluate this decision by
// measuring the performance of an equivalent implementation using
// "simpler" instructions
masm->bind(&fast);
masm->bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
bind(&fast);
bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
}
class RecordWriteStub : public CodeStub {
public:
RecordWriteStub(Register object, Register addr, Register scratch)
: object_(object), addr_(addr), scratch_(scratch) { }
void Generate(MacroAssembler* masm);
private:
Register object_;
Register addr_;
Register scratch_;
#ifdef DEBUG
void Print() {
PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
object_.code(), addr_.code(), scratch_.code());
void MacroAssembler::InNewSpace(Register object,
Register scratch,
Condition cc,
Label* branch) {
if (Serializer::enabled()) {
// Can't do arithmetic on external references if it might get serialized.
mov(scratch, Operand(object));
// The mask isn't really an address. We load it as an external reference in
// case the size of the new space is different between the snapshot maker
// and the running system.
and_(Operand(scratch), Immediate(ExternalReference::new_space_mask()));
cmp(Operand(scratch), Immediate(ExternalReference::new_space_start()));
j(cc, branch);
} else {
int32_t new_space_start = reinterpret_cast<int32_t>(
ExternalReference::new_space_start().address());
lea(scratch, Operand(object, -new_space_start));
and_(scratch, Heap::NewSpaceMask());
j(cc, branch);
}
#endif
// Minor key encoding in 12 bits of three registers (object, address and
// scratch) OOOOAAAASSSS.
class ScratchBits: public BitField<uint32_t, 0, 4> {};
class AddressBits: public BitField<uint32_t, 4, 4> {};
class ObjectBits: public BitField<uint32_t, 8, 4> {};
Major MajorKey() { return RecordWrite; }
int MinorKey() {
// Encode the registers.
return ObjectBits::encode(object_.code()) |
AddressBits::encode(addr_.code()) |
ScratchBits::encode(scratch_.code());
}
};
void RecordWriteStub::Generate(MacroAssembler* masm) {
RecordWriteHelper(masm, object_, addr_, scratch_);
masm->ret(0);
}
@ -161,22 +141,7 @@ void MacroAssembler::RecordWrite(Register object, int offset,
test(value, Immediate(kSmiTagMask));
j(zero, &done);
if (Serializer::enabled()) {
// Can't do arithmetic on external references if it might get serialized.
mov(value, Operand(object));
// The mask isn't really an address. We load it as an external reference in
// case the size of the new space is different between the snapshot maker
// and the running system.
and_(Operand(value), Immediate(ExternalReference::new_space_mask()));
cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
j(equal, &done);
} else {
int32_t new_space_start = reinterpret_cast<int32_t>(
ExternalReference::new_space_start().address());
lea(value, Operand(object, -new_space_start));
and_(value, Heap::NewSpaceMask());
j(equal, &done);
}
InNewSpace(object, value, equal, &done);
if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
// Compute the bit offset in the remembered set, leave it in 'value'.
@ -209,7 +174,7 @@ void MacroAssembler::RecordWrite(Register object, int offset,
// If we are already generating a shared stub, not inlining the
// record write code isn't going to save us any memory.
if (generating_stub()) {
RecordWriteHelper(this, object, dst, value);
RecordWriteHelper(object, dst, value);
} else {
RecordWriteStub stub(object, dst, value);
CallStub(&stub);

View File

@ -48,6 +48,18 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// GC Support
void RecordWriteHelper(Register object,
Register addr,
Register scratch);
// Check if object is in new space.
// scratch can be object itself, but it will be clobbered.
void InNewSpace(Register object,
Register scratch,
Condition cc, // equal for new space, not_equal otherwise.
Label* branch);
// Set the remembered set bit for [object+offset].
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the scratch register contains the array index into

View File

@ -549,9 +549,8 @@ class CallOptimization BASE_EMBEDDED {
// fast api call builtin.
void AnalyzePossibleApiFunction(JSFunction* function) {
SharedFunctionInfo* sfi = function->shared();
if (sfi->function_data()->IsUndefined()) return;
FunctionTemplateInfo* info =
FunctionTemplateInfo::cast(sfi->function_data());
if (!sfi->IsApiFunction()) return;
FunctionTemplateInfo* info = sfi->get_api_func_data();
// Require a C++ callback.
if (info->call_code()->IsUndefined()) return;
@ -1210,6 +1209,107 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
}
Object* CallStubCompiler::CompileArrayPushCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
Label miss;
// Get the receiver from the stack.
const int argc = arguments().immediate();
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
// Check that the receiver isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken);
CheckPrototypes(JSObject::cast(object), edx,
holder, ebx,
eax, name, &miss);
if (argc == 0) {
// Noop, return the length.
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
__ ret((argc + 1) * kPointerSize);
} else {
// Get the elements array of the object.
__ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
// Check that the elements are in fast mode (not dictionary).
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::fixed_array_map()));
__ j(not_equal, &miss, not_taken);
if (argc == 1) { // Otherwise fall through to call builtin.
Label call_builtin, exit, with_rset_update;
// Get the array's length into eax and calculate new length.
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
__ add(Operand(eax), Immediate(argc << 1));
// Get the element's length into ecx.
__ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
__ SmiTag(ecx);
// Check if we could survive without allocation, go to builtin otherwise.
__ cmp(eax, Operand(ecx));
__ j(greater, &call_builtin);
// Save new length.
__ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
// Push the element.
__ lea(edx, FieldOperand(ebx,
eax, times_half_pointer_size,
FixedArray::kHeaderSize - argc * kPointerSize));
__ mov(ecx, Operand(esp, argc * kPointerSize));
__ mov(Operand(edx, 0), ecx);
// Check if wrote not a smi.
__ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &with_rset_update);
__ bind(&exit);
__ ret((argc + 1) * kPointerSize);
__ bind(&with_rset_update);
__ InNewSpace(ebx, ecx, equal, &exit);
RecordWriteStub stub(ebx, edx, ecx);
__ CallStub(&stub);
__ ret((argc + 1) * kPointerSize);
__ bind(&call_builtin);
}
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
argc + 1,
1);
}
__ bind(&miss);
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
__ jmp(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
String* function_name = NULL;
if (function->shared()->name()->IsString()) {
function_name = String::cast(function->shared()->name());
}
return GetCode(CONSTANT_FUNCTION, function_name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
@ -1222,6 +1322,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
CustomCallGenerator generator =
ToCData<CustomCallGenerator>(function_info->function_data());
return generator(this, object, holder, function, name, check);
}
Label miss_in_smi_check;
// Get the receiver from the stack.

View File

@ -1346,10 +1346,9 @@ void Logger::LogCompiledFunctions() {
LOG(CodeCreateEvent(
Logger::LAZY_COMPILE_TAG, shared->code(), *func_name));
}
} else if (shared->function_data()->IsFunctionTemplateInfo()) {
} else if (shared->IsApiFunction()) {
// API function.
FunctionTemplateInfo* fun_data =
FunctionTemplateInfo::cast(shared->function_data());
FunctionTemplateInfo* fun_data = shared->get_api_func_data();
Object* raw_call_data = fun_data->call_code();
if (!raw_call_data->IsUndefined()) {
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);

View File

@ -789,7 +789,7 @@ void SharedFunctionInfo::SharedFunctionInfoVerify() {
VerifyObjectField(kNameOffset);
VerifyObjectField(kCodeOffset);
VerifyObjectField(kInstanceClassNameOffset);
VerifyObjectField(kExternalReferenceDataOffset);
VerifyObjectField(kFunctionDataOffset);
VerifyObjectField(kScriptOffset);
VerifyObjectField(kDebugInfoOffset);
}

View File

@ -2361,8 +2361,7 @@ ACCESSORS(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset)
ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset)
ACCESSORS(SharedFunctionInfo, instance_class_name, Object,
kInstanceClassNameOffset)
ACCESSORS(SharedFunctionInfo, function_data, Object,
kExternalReferenceDataOffset)
ACCESSORS(SharedFunctionInfo, function_data, Object, kFunctionDataOffset)
ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
@ -2453,6 +2452,22 @@ bool SharedFunctionInfo::is_compiled() {
}
bool SharedFunctionInfo::IsApiFunction() {
return function_data()->IsFunctionTemplateInfo();
}
FunctionTemplateInfo* SharedFunctionInfo::get_api_func_data() {
ASSERT(IsApiFunction());
return FunctionTemplateInfo::cast(function_data());
}
bool SharedFunctionInfo::HasCustomCallGenerator() {
return function_data()->IsProxy();
}
bool JSFunction::IsBoilerplate() {
return map() == Heap::boilerplate_function_map();
}

View File

@ -480,7 +480,7 @@ bool JSObject::IsDirty() {
if (!cons_obj->IsJSFunction())
return true;
JSFunction* fun = JSFunction::cast(cons_obj);
if (!fun->shared()->function_data()->IsFunctionTemplateInfo())
if (!fun->shared()->IsApiFunction())
return true;
// If the object is fully fast case and has the same map it was
// created with then no changes can have been made to it.
@ -6433,9 +6433,9 @@ void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
InterceptorInfo* JSObject::GetNamedInterceptor() {
ASSERT(map()->has_named_interceptor());
JSFunction* constructor = JSFunction::cast(map()->constructor());
Object* template_info = constructor->shared()->function_data();
ASSERT(constructor->shared()->IsApiFunction());
Object* result =
FunctionTemplateInfo::cast(template_info)->named_property_handler();
constructor->shared()->get_api_func_data()->named_property_handler();
return InterceptorInfo::cast(result);
}
@ -6443,9 +6443,9 @@ InterceptorInfo* JSObject::GetNamedInterceptor() {
InterceptorInfo* JSObject::GetIndexedInterceptor() {
ASSERT(map()->has_indexed_interceptor());
JSFunction* constructor = JSFunction::cast(map()->constructor());
Object* template_info = constructor->shared()->function_data();
ASSERT(constructor->shared()->IsApiFunction());
Object* result =
FunctionTemplateInfo::cast(template_info)->indexed_property_handler();
constructor->shared()->get_api_func_data()->indexed_property_handler();
return InterceptorInfo::cast(result);
}

View File

@ -3186,12 +3186,18 @@ class SharedFunctionInfo: public HeapObject {
// [instance class name]: class name for instances.
DECL_ACCESSORS(instance_class_name, Object)
// [function data]: This field has been added for make benefit the API.
// [function data]: This field holds some additional data for function.
// Currently it either has FunctionTemplateInfo to make benefit the API
// or Proxy wrapping CustomCallGenerator.
// In the long run we don't want all functions to have this field but
// we can fix that when we have a better model for storing hidden data
// on objects.
DECL_ACCESSORS(function_data, Object)
inline bool IsApiFunction();
inline FunctionTemplateInfo* get_api_func_data();
inline bool HasCustomCallGenerator();
// [script info]: Script from which the function originates.
DECL_ACCESSORS(script, Object)
@ -3299,9 +3305,9 @@ class SharedFunctionInfo: public HeapObject {
static const int kConstructStubOffset = kCodeOffset + kPointerSize;
static const int kInstanceClassNameOffset =
kConstructStubOffset + kPointerSize;
static const int kExternalReferenceDataOffset =
static const int kFunctionDataOffset =
kInstanceClassNameOffset + kPointerSize;
static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize;
static const int kScriptOffset = kFunctionDataOffset + kPointerSize;
static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
static const int kThisPropertyAssignmentsOffset =

View File

@ -1964,9 +1964,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
Factory::NewFunctionBoilerplate(name, literals, code);
boilerplate->shared()->set_construct_stub(*construct_stub);
// Copy the function data to the boilerplate. Used by
// builtins.cc:HandleApiCall to perform argument type checks and to
// find the right native code to call.
// Copy the function data to the boilerplate.
boilerplate->shared()->set_function_data(fun->shared()->function_data());
int parameters = fun->shared()->formal_parameter_count();
boilerplate->shared()->set_formal_parameter_count(parameters);

View File

@ -1238,6 +1238,60 @@ static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
}
static void SetCustomCallGenerator(Handle<JSFunction> function,
CustomCallGenerator generator) {
if (function->shared()->function_data()->IsUndefined()) {
function->shared()->set_function_data(*FromCData(generator));
}
}
static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
const char* name,
Builtins::Name builtin_name,
CustomCallGenerator generator = NULL) {
Handle<String> key = Factory::LookupAsciiSymbol(name);
Handle<Code> code(Builtins::builtin(builtin_name));
Handle<JSFunction> optimized = Factory::NewFunction(key,
JS_OBJECT_TYPE,
JSObject::kHeaderSize,
code,
false);
optimized->shared()->DontAdaptArguments();
if (generator != NULL) {
SetCustomCallGenerator(optimized, generator);
}
SetProperty(holder, key, optimized, NONE);
return optimized;
}
static Object* CompileArrayPushCall(CallStubCompiler* compiler,
Object* object,
JSObject* holder,
JSFunction* function,
String* name,
StubCompiler::CheckType check) {
return compiler->CompileArrayPushCall(object, holder, function, name, check);
}
static Object* Runtime_SpecialArrayFunctions(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, holder, 0);
InstallBuiltin(holder, "pop", Builtins::ArrayPop);
InstallBuiltin(holder, "push", Builtins::ArrayPush, CompileArrayPushCall);
InstallBuiltin(holder, "shift", Builtins::ArrayShift);
InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
InstallBuiltin(holder, "slice", Builtins::ArraySlice);
InstallBuiltin(holder, "splice", Builtins::ArraySplice);
return *holder;
}
static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 4);
@ -1372,10 +1426,8 @@ static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSFunction, f, args[0]);
// The function_data field of the shared function info is used exclusively by
// the API.
return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
: Heap::false_value();
return f->shared()->IsApiFunction() ? Heap::true_value()
: Heap::false_value();
}
static Object* Runtime_FunctionIsBuiltin(Arguments args) {

View File

@ -61,6 +61,7 @@ namespace internal {
F(ToFastProperties, 1, 1) \
F(ToSlowProperties, 1, 1) \
F(FinishArrayPrototypeSetup, 1, 1) \
F(SpecialArrayFunctions, 1, 1) \
\
F(IsInPrototypeChain, 2, 1) \
F(SetHiddenPrototype, 2, 1) \

View File

@ -548,7 +548,7 @@ class KeyedStoreStubCompiler: public StubCompiler {
class CallStubCompiler: public StubCompiler {
public:
explicit CallStubCompiler(int argc, InLoopFlag in_loop)
CallStubCompiler(int argc, InLoopFlag in_loop)
: arguments_(argc), in_loop_(in_loop) { }
Object* CompileCallField(JSObject* object,
@ -569,6 +569,12 @@ class CallStubCompiler: public StubCompiler {
JSFunction* function,
String* name);
Object* CompileArrayPushCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check);
private:
const ParameterCount arguments_;
const InLoopFlag in_loop_;
@ -590,6 +596,14 @@ class ConstructStubCompiler: public StubCompiler {
};
typedef Object* (*CustomCallGenerator)(CallStubCompiler* compiler,
Object* object,
JSObject* holder,
JSFunction* function,
String* name,
StubCompiler::CheckType check);
} } // namespace v8::internal
#endif // V8_STUB_CACHE_H_

View File

@ -439,10 +439,9 @@ void Top::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
// Get the data object from access check info.
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
Object* info = constructor->shared()->function_data();
if (info == Heap::undefined_value()) return;
Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
if (!constructor->shared()->IsApiFunction()) return;
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == Heap::undefined_value()) return;
HandleScope scope;
@ -502,10 +501,10 @@ bool Top::MayNamedAccess(JSObject* receiver, Object* key, v8::AccessType type) {
// Get named access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
Object* info = constructor->shared()->function_data();
if (info == Heap::undefined_value()) return false;
if (!constructor->shared()->IsApiFunction()) return false;
Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == Heap::undefined_value()) return false;
Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
@ -547,10 +546,10 @@ bool Top::MayIndexedAccess(JSObject* receiver,
// Get indexed access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
Object* info = constructor->shared()->function_data();
if (info == Heap::undefined_value()) return false;
if (!constructor->shared()->IsApiFunction()) return false;
Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
Object* data_obj =
constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == Heap::undefined_value()) return false;
Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();

View File

@ -655,6 +655,63 @@ class CallInterceptorCompiler BASE_EMBEDDED {
#define __ ACCESS_MASM((masm()))
Object* CallStubCompiler::CompileArrayPushCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
// rsp[8] : argument argc
// rsp[16] : argument argc - 1
// ...
// rsp[argc * 8] : argument 1
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
// TODO(639): faster implementation.
ASSERT(check == RECEIVER_MAP_CHECK);
Label miss;
// Get the receiver from the stack.
const int argc = arguments().immediate();
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
// Check that the receiver isn't a smi.
__ JumpIfSmi(rdx, &miss);
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), rdx, holder,
rbx, rax, name, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.
if (object->IsGlobalObject()) {
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
__ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
}
__ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
argc + 1,
1);
// Handle call cache miss.
__ bind(&miss);
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
__ Jump(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
String* function_name = NULL;
if (function->shared()->name()->IsString()) {
function_name = String::cast(function->shared()->name());
}
return GetCode(CONSTANT_FUNCTION, function_name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
@ -670,6 +727,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
SharedFunctionInfo* function_info = function->shared();
if (function_info->HasCustomCallGenerator()) {
CustomCallGenerator generator =
ToCData<CustomCallGenerator>(function_info->function_data());
return generator(this, object, holder, function, name, check);
}
Label miss;
// Get the receiver from the stack.

View File

@ -0,0 +1,68 @@
// 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.
// Check pushes with various number of arguments.
(function() {
var a = [];
for (var i = 0; i < 7; i++) {
a = [];
assertEquals(0, a.push());
assertEquals([], a, "after .push()");
assertEquals(1, a.push(1), "length after .push(1)");
assertEquals([1], a, "after .push(1)");
assertEquals(3, a.push(2, 3), "length after .push(2, 3)");
assertEquals([1, 2, 3], a, "after .push(2, 3)");
assertEquals(6, a.push(4, 5, 6),
"length after .push(4, 5, 6)");
assertEquals([1, 2, 3, 4, 5, 6], a,
"after .push(4, 5, 5)");
assertEquals(10, a.push(7, 8, 9, 10),
"length after .push(7, 8, 9, 10)");
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a,
"after .push(7, 8, 9, 10)");
assertEquals(15, a.push(11, 12, 13, 14, 15),
"length after .push(11, 12, 13, 14, 15)");
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], a,
"after .push(11, 12, 13, 14, 15)");
assertEquals(21, a.push(16, 17, 18, 19, 20, 21),
"length after .push(16, 17, 18, 19, 20, 21)");
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], a,
"after .push(16, 17, 18, 19, 20, 21)");
assertEquals(28, a.push(22, 23, 24, 25, 26, 27, 28),
"length hafter .push(22, 23, 24, 25, 26, 27, 28)");
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], a,
"after .push(22, 23, 24, 25, 26, 27, 28)");
}
})();