V(ConvertToDouble) \
V(WriteInt32ToHeapNumber) \
V(StackCheck) \
+ V(FastNewClosure) \
V(UnarySub) \
V(RevertToNumber) \
V(ToBoolean) \
};
+class FastNewClosureStub : public CodeStub {
+ public:
+ void Generate(MacroAssembler* masm);
+
+ private:
+ const char* GetName() { return "FastNewClosureStub"; }
+ Major MajorKey() { return FastNewClosure; }
+ int MinorKey() { return 0; }
+};
+
+
class InstanceofStub: public CodeStub {
public:
InstanceofStub() { }
Handle<JSFunction> Factory::BaseNewFunctionFromBoilerplate(
Handle<JSFunction> boilerplate,
- Handle<Map> function_map) {
+ Handle<Map> function_map,
+ PretenureFlag pretenure) {
ASSERT(boilerplate->IsBoilerplate());
ASSERT(!boilerplate->has_initial_map());
ASSERT(!boilerplate->has_prototype());
ASSERT(boilerplate->elements() == Heap::empty_fixed_array());
CALL_HEAP_FUNCTION(Heap::AllocateFunction(*function_map,
boilerplate->shared(),
- Heap::the_hole_value()),
+ Heap::the_hole_value(),
+ pretenure),
JSFunction);
}
Handle<JSFunction> Factory::NewFunctionFromBoilerplate(
Handle<JSFunction> boilerplate,
- Handle<Context> context) {
- Handle<JSFunction> result =
- BaseNewFunctionFromBoilerplate(boilerplate, Top::function_map());
+ Handle<Context> context,
+ PretenureFlag pretenure) {
+ Handle<JSFunction> result = BaseNewFunctionFromBoilerplate(
+ boilerplate, Top::function_map(), pretenure);
result->set_context(*context);
int number_of_literals = boilerplate->NumberOfLiterals();
Handle<FixedArray> literals =
- Factory::NewFixedArray(number_of_literals, TENURED);
+ Factory::NewFixedArray(number_of_literals, pretenure);
if (number_of_literals > 0) {
// Store the object, regexp and array functions in the literals
// array prefix. These functions will be used when creating
static Handle<JSFunction> NewFunctionFromBoilerplate(
Handle<JSFunction> boilerplate,
- Handle<Context> context);
+ Handle<Context> context,
+ PretenureFlag pretenure = TENURED);
static Handle<Code> NewCode(const CodeDesc& desc,
ZoneScopeInfo* sinfo,
static Handle<JSFunction> BaseNewFunctionFromBoilerplate(
Handle<JSFunction> boilerplate,
- Handle<Map> function_map);
+ Handle<Map> function_map,
+ PretenureFlag pretenure);
// Create a new map cache.
static Handle<MapCache> NewMapCache(int at_least_space_for);
Object* Heap::AllocateFunction(Map* function_map,
SharedFunctionInfo* shared,
- Object* prototype) {
- Object* result = Allocate(function_map, OLD_POINTER_SPACE);
+ Object* prototype,
+ PretenureFlag pretenure) {
+ AllocationSpace space =
+ (pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
+ Object* result = Allocate(function_map, space);
if (result->IsFailure()) return result;
return InitializeFunction(JSFunction::cast(result), shared, prototype);
}
// Please note this does not perform a garbage collection.
static Object* AllocateFunction(Map* function_map,
SharedFunctionInfo* shared,
- Object* prototype);
+ Object* prototype,
+ PretenureFlag pretenure = TENURED);
// Indicies for direct access into argument objects.
static const int arguments_callee_index = 0;
void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
- // Call the runtime to instantiate the function boilerplate object.
- // The inevitable call will sync frame elements to memory anyway, so
- // we do it eagerly to allow us to push the arguments directly into
- // place.
ASSERT(boilerplate->IsBoilerplate());
- frame_->SyncRange(0, frame_->element_count() - 1);
- // Create a new closure.
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(boilerplate));
- Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
- frame_->Push(&result);
+ // Use the fast case closure allocation code that allocated in new
+ // space for nested functions that don't need literals cloning.
+ if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() == 0) {
+ FastNewClosureStub stub;
+ frame_->Push(boilerplate);
+ Result answer = frame_->CallStub(&stub, 1);
+ frame_->Push(&answer);
+ } else {
+ // Call the runtime to instantiate the function boilerplate
+ // object. The inevitable call will sync frame elements to memory
+ // anyway, so we do it eagerly to allow us to push the arguments
+ // directly into place.
+ frame_->SyncRange(0, frame_->element_count() - 1);
+
+ // Create a new closure.
+ frame_->EmitPush(esi);
+ frame_->EmitPush(Immediate(boilerplate));
+ Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
+ frame_->Push(&result);
+ }
}
}
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+ // Clone the boilerplate in new space. Set the context to the
+ // current context in esi.
+ Label gc;
+ __ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
+
+ // Get the boilerplate function from the stack.
+ __ mov(edx, Operand(esp, 1 * kPointerSize));
+
+ // Compute the function map in the current global context and set that
+ // as the map of the allocated object.
+ __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalContextOffset));
+ __ mov(ecx, Operand(ecx, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
+ __ mov(FieldOperand(eax, JSObject::kMapOffset), ecx);
+
+ // Clone the rest of the boilerplate fields. We don't have to update
+ // the write barrier because the allocated object is in new space.
+ for (int offset = kPointerSize;
+ offset < JSFunction::kSize;
+ offset += kPointerSize) {
+ if (offset == JSFunction::kContextOffset) {
+ __ mov(FieldOperand(eax, offset), esi);
+ } else {
+ __ mov(ebx, FieldOperand(edx, offset));
+ __ mov(FieldOperand(eax, offset), ebx);
+ }
+ }
+
+ // Return and remove the on-stack parameter.
+ __ ret(1 * kPointerSize);
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ pop(ecx); // Temporarily remove return address.
+ __ pop(edx);
+ __ push(esi);
+ __ push(edx);
+ __ push(ecx); // Restore return address.
+ __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+}
+
+
// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
void ToBooleanStub::Generate(MacroAssembler* masm) {
Label false_result, true_result, not_string;
__ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
- __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
- __ j(not_equal, &miss, not_taken);
+ 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);
+ }
// Patch the receiver on the stack with the global proxy.
if (object->IsGlobalObject()) {
// Range check.
ASSERT(descriptor_number < number_of_descriptors());
- // Make sure non of the elements in desc are in new space.
+ // Make sure none of the elements in desc are in new space.
ASSERT(!Heap::InNewSpace(desc->GetKey()));
ASSERT(!Heap::InNewSpace(desc->GetValue()));
Object* JSObject::AddConstantFunctionProperty(String* name,
JSFunction* function,
PropertyAttributes attributes) {
+ ASSERT(!Heap::InNewSpace(function));
+
// Allocate new instance descriptors with (name, function) added
ConstantFunctionDescriptor d(name, function, attributes);
Object* new_descriptors =
// Ensure the descriptor array does not get too big.
if (map()->instance_descriptors()->number_of_descriptors() <
DescriptorArray::kMaxNumberOfDescriptors) {
- if (value->IsJSFunction()) {
+ if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
return AddConstantFunctionProperty(name,
JSFunction::cast(value),
attributes);
return Heap::empty_descriptor_array();
}
// Allocate the array of keys.
- Object* array = Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
+ Object* array =
+ Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
if (array->IsFailure()) return array;
// Do not use DescriptorArray::cast on incomplete object.
FixedArray* result = FixedArray::cast(array);
PropertyType type = DetailsAt(i).type();
ASSERT(type != FIELD);
instance_descriptor_length++;
- if (type == NORMAL && !value->IsJSFunction()) number_of_fields += 1;
+ if (type == NORMAL &&
+ (!value->IsJSFunction() || Heap::InNewSpace(value))) {
+ number_of_fields += 1;
+ }
}
}
PropertyDetails details = DetailsAt(i);
PropertyType type = details.type();
- if (value->IsJSFunction()) {
+ if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
ConstantFunctionDescriptor d(String::cast(key),
JSFunction::cast(value),
details.attributes(),
public:
// Is this the singleton empty_descriptor_array?
inline bool IsEmpty();
+
// Returns the number of descriptors in the array.
int number_of_descriptors() {
return IsEmpty() ? 0 : length() - kFirstIndex;
static int ToKeyIndex(int descriptor_number) {
return descriptor_number+kFirstIndex;
}
- static int ToValueIndex(int descriptor_number) {
- return descriptor_number << 1;
- }
+
static int ToDetailsIndex(int descriptor_number) {
return( descriptor_number << 1) + 1;
}
+ static int ToValueIndex(int descriptor_number) {
+ return descriptor_number << 1;
+ }
+
bool is_null_descriptor(int descriptor_number) {
return PropertyDetails(GetDetails(descriptor_number)).type() ==
NULL_DESCRIPTOR;
// Copy the function and update its context. Use it as value.
Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
Handle<JSFunction> function =
- Factory::NewFunctionFromBoilerplate(boilerplate, context);
+ Factory::NewFunctionFromBoilerplate(boilerplate, context, TENURED);
value = function;
}
CONVERT_ARG_CHECKED(Context, context, 0);
CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
+ PretenureFlag pretenure = (context->global_context() == *context)
+ ? TENURED // Allocate global closures in old space.
+ : NOT_TENURED; // Allocate local closures in new space.
Handle<JSFunction> result =
- Factory::NewFunctionFromBoilerplate(boilerplate, context);
+ Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
return *result;
}
validate);
if (boilerplate.is_null()) return Failure::Exception();
Handle<JSFunction> fun =
- Factory::NewFunctionFromBoilerplate(boilerplate, context);
+ Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
return *fun;
}
Compiler::DONT_VALIDATE_JSON);
if (boilerplate.is_null()) return Failure::Exception();
Handle<JSFunction> fun =
- Factory::NewFunctionFromBoilerplate(boilerplate, context);
+ Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
return *fun;
}