Revert r14930 and r14935 temporarily.
This is a temporary revert to track down a potential perf regression introduced in r14930. The following two changes were reverted: - "Deprecate HAllocateObject in favor of HAllocate." - "Added pretenuring support for call new." R=danno@chromium.org BUG=chromium:247504 Review URL: https://codereview.chromium.org/17491002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15229 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
d51a0d06e3
commit
ea87d08557
@ -2420,6 +2420,14 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LAllocateObject* result =
|
||||
new(zone()) LAllocateObject(TempRegister(), TempRegister());
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LOperand* size = instr->size()->IsConstant()
|
||||
|
@ -49,6 +49,7 @@ class LCodeGen;
|
||||
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(AllocateObject) \
|
||||
V(Allocate) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
@ -2444,6 +2445,21 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 1, 2> {
|
||||
public:
|
||||
LAllocateObject(LOperand* temp, LOperand* temp2) {
|
||||
temps_[0] = temp;
|
||||
temps_[1] = temp2;
|
||||
}
|
||||
|
||||
LOperand* temp() { return temps_[0]; }
|
||||
LOperand* temp2() { return temps_[1]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LAllocate: public LTemplateInstruction<1, 2, 2> {
|
||||
public:
|
||||
LAllocate(LOperand* size, LOperand* temp1, LOperand* temp2) {
|
||||
|
@ -5365,6 +5365,80 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred =
|
||||
new(zone()) DeferredAllocateObject(this, instr);
|
||||
|
||||
Register result = ToRegister(instr->result());
|
||||
Register scratch = ToRegister(instr->temp());
|
||||
Register scratch2 = ToRegister(instr->temp2());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
ASSERT(initial_map->pre_allocated_property_fields() +
|
||||
initial_map->unused_property_fields() -
|
||||
initial_map->inobject_properties() == 0);
|
||||
|
||||
__ Allocate(instance_size, result, scratch, scratch2, deferred->entry(),
|
||||
TAG_OBJECT);
|
||||
|
||||
__ bind(deferred->exit());
|
||||
if (FLAG_debug_code) {
|
||||
Label is_in_new_space;
|
||||
__ JumpIfInNewSpace(result, scratch, &is_in_new_space);
|
||||
__ Abort("Allocated object is not in new-space");
|
||||
__ bind(&is_in_new_space);
|
||||
}
|
||||
|
||||
// Load the initial map.
|
||||
Register map = scratch;
|
||||
__ LoadHeapObject(map, constructor);
|
||||
__ ldr(map, FieldMemOperand(map, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
// Initialize map and fields of the newly allocated object.
|
||||
ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
|
||||
__ str(map, FieldMemOperand(result, JSObject::kMapOffset));
|
||||
__ LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ str(scratch, FieldMemOperand(result, JSObject::kElementsOffset));
|
||||
__ str(scratch, FieldMemOperand(result, JSObject::kPropertiesOffset));
|
||||
if (initial_map->inobject_properties() != 0) {
|
||||
__ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
|
||||
for (int i = 0; i < initial_map->inobject_properties(); i++) {
|
||||
int property_offset = JSObject::kHeaderSize + i * kPointerSize;
|
||||
__ str(scratch, FieldMemOperand(result, property_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ mov(result, Operand::Zero());
|
||||
|
||||
PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
|
||||
__ mov(r0, Operand(Smi::FromInt(instance_size)));
|
||||
__ push(r0);
|
||||
CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
|
||||
__ StoreToSafepointRegisterSlot(r0, result);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocate(LAllocate* instr) {
|
||||
class DeferredAllocate: public LDeferredCode {
|
||||
public:
|
||||
|
@ -151,6 +151,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredRandom(LRandom* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredAllocate(LAllocate* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
@ -195,9 +195,6 @@ DEFINE_bool(clever_optimizations,
|
||||
true,
|
||||
"Optimize object size, Array shift, DOM strings and string +")
|
||||
DEFINE_bool(pretenuring, true, "allocate objects in old space")
|
||||
// TODO(hpayer): We will remove this flag as soon as we have pretenuring
|
||||
// support for specific allocation sites.
|
||||
DEFINE_bool(pretenuring_call_new, false, "pretenure call new")
|
||||
DEFINE_bool(track_fields, true, "track fields with only smi values")
|
||||
DEFINE_bool(track_double_fields, true, "track fields with double values")
|
||||
DEFINE_bool(track_heap_object_fields, true, "track fields with heap values")
|
||||
|
10
src/heap.cc
10
src/heap.cc
@ -4421,7 +4421,10 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) {
|
||||
ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);
|
||||
|
||||
// Allocate the backing storage for the properties.
|
||||
int prop_size = map->InitialPropertiesLength();
|
||||
int prop_size =
|
||||
map->pre_allocated_property_fields() +
|
||||
map->unused_property_fields() -
|
||||
map->inobject_properties();
|
||||
ASSERT(prop_size >= 0);
|
||||
Object* properties;
|
||||
{ MaybeObject* maybe_properties = AllocateFixedArray(prop_size, pretenure);
|
||||
@ -4458,7 +4461,10 @@ MaybeObject* Heap::AllocateJSObjectFromMapWithAllocationSite(Map* map,
|
||||
ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);
|
||||
|
||||
// Allocate the backing storage for the properties.
|
||||
int prop_size = map->InitialPropertiesLength();
|
||||
int prop_size =
|
||||
map->pre_allocated_property_fields() +
|
||||
map->unused_property_fields() -
|
||||
map->inobject_properties();
|
||||
ASSERT(prop_size >= 0);
|
||||
Object* properties;
|
||||
{ MaybeObject* maybe_properties = AllocateFixedArray(prop_size);
|
||||
|
@ -3143,6 +3143,11 @@ HType HStringCharFromCode::CalculateInferredType() {
|
||||
}
|
||||
|
||||
|
||||
HType HAllocateObject::CalculateInferredType() {
|
||||
return HType::JSObject();
|
||||
}
|
||||
|
||||
|
||||
HType HAllocate::CalculateInferredType() {
|
||||
return type_;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ class LChunkBuilder;
|
||||
V(AccessArgumentsAt) \
|
||||
V(Add) \
|
||||
V(Allocate) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -4927,6 +4928,48 @@ class HLoadGlobalGeneric: public HTemplateInstruction<2> {
|
||||
};
|
||||
|
||||
|
||||
class HAllocateObject: public HTemplateInstruction<1> {
|
||||
public:
|
||||
HAllocateObject(HValue* context, Handle<JSFunction> constructor)
|
||||
: constructor_(constructor) {
|
||||
SetOperandAt(0, context);
|
||||
set_representation(Representation::Tagged());
|
||||
SetGVNFlag(kChangesNewSpacePromotion);
|
||||
constructor_initial_map_ = constructor->has_initial_map()
|
||||
? Handle<Map>(constructor->initial_map())
|
||||
: Handle<Map>::null();
|
||||
// If slack tracking finished, the instance size and property counts
|
||||
// remain unchanged so that we can allocate memory for the object.
|
||||
ASSERT(!constructor->shared()->IsInobjectSlackTrackingInProgress());
|
||||
}
|
||||
|
||||
// Maximum instance size for which allocations will be inlined.
|
||||
static const int kMaxSize = 64 * kPointerSize;
|
||||
|
||||
HValue* context() { return OperandAt(0); }
|
||||
Handle<JSFunction> constructor() { return constructor_; }
|
||||
Handle<Map> constructor_initial_map() { return constructor_initial_map_; }
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) {
|
||||
return Representation::Tagged();
|
||||
}
|
||||
virtual Handle<Map> GetMonomorphicJSObjectMap() {
|
||||
ASSERT(!constructor_initial_map_.is_null());
|
||||
return constructor_initial_map_;
|
||||
}
|
||||
virtual HType CalculateInferredType();
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject)
|
||||
|
||||
private:
|
||||
// TODO(svenpanne) Might be safe, but leave it out until we know for sure.
|
||||
// virtual bool IsDeletable() const { return true; }
|
||||
|
||||
Handle<JSFunction> constructor_;
|
||||
Handle<Map> constructor_initial_map_;
|
||||
};
|
||||
|
||||
|
||||
class HAllocate: public HTemplateInstruction<2> {
|
||||
public:
|
||||
enum Flags {
|
||||
@ -4945,9 +4988,6 @@ class HAllocate: public HTemplateInstruction<2> {
|
||||
SetGVNFlag(kChangesNewSpacePromotion);
|
||||
}
|
||||
|
||||
// Maximum instance size for which allocations will be inlined.
|
||||
static const int kMaxInlineSize = 64 * kPointerSize;
|
||||
|
||||
static Flags DefaultFlags() {
|
||||
return CAN_ALLOCATE_IN_NEW_SPACE;
|
||||
}
|
||||
@ -4972,14 +5012,6 @@ class HAllocate: public HTemplateInstruction<2> {
|
||||
}
|
||||
}
|
||||
|
||||
virtual Handle<Map> GetMonomorphicJSObjectMap() {
|
||||
return known_initial_map_;
|
||||
}
|
||||
|
||||
void set_known_initial_map(Handle<Map> known_initial_map) {
|
||||
known_initial_map_ = known_initial_map;
|
||||
}
|
||||
|
||||
virtual HType CalculateInferredType();
|
||||
|
||||
bool CanAllocateInNewSpace() const {
|
||||
@ -5014,7 +5046,6 @@ class HAllocate: public HTemplateInstruction<2> {
|
||||
private:
|
||||
HType type_;
|
||||
Flags flags_;
|
||||
Handle<Map> known_initial_map_;
|
||||
};
|
||||
|
||||
|
||||
@ -5058,6 +5089,7 @@ inline bool ReceiverObjectNeedsWriteBarrier(HValue* object,
|
||||
new_space_dominator);
|
||||
}
|
||||
if (object != new_space_dominator) return true;
|
||||
if (object->IsAllocateObject()) return false;
|
||||
if (object->IsAllocate()) {
|
||||
return !HAllocate::cast(object)->GuaranteedInNewSpace();
|
||||
}
|
||||
|
@ -8827,8 +8827,7 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
||||
static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
||||
return constructor->has_initial_map() &&
|
||||
constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
|
||||
constructor->initial_map()->instance_size() < HAllocate::kMaxInlineSize &&
|
||||
constructor->initial_map()->InitialPropertiesLength() == 0;
|
||||
constructor->initial_map()->instance_size() < HAllocateObject::kMaxSize;
|
||||
}
|
||||
|
||||
|
||||
@ -8838,7 +8837,6 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
||||
ASSERT(current_block()->HasPredecessor());
|
||||
int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
||||
HValue* context = environment()->LookupContext();
|
||||
Factory* factory = isolate()->factory();
|
||||
|
||||
if (FLAG_inline_construct &&
|
||||
expr->IsMonomorphic() &&
|
||||
@ -8858,81 +8856,20 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
||||
constructor->shared()->CompleteInobjectSlackTracking();
|
||||
}
|
||||
|
||||
// Calculate instance size from initial map of constructor.
|
||||
ASSERT(constructor->has_initial_map());
|
||||
Handle<Map> initial_map(constructor->initial_map());
|
||||
int instance_size = initial_map->instance_size();
|
||||
ASSERT(initial_map->InitialPropertiesLength() == 0);
|
||||
|
||||
// Allocate an instance of the implicit receiver object.
|
||||
HValue* size_in_bytes =
|
||||
AddInstruction(new(zone()) HConstant(instance_size));
|
||||
|
||||
HAllocate::Flags flags = HAllocate::DefaultFlags();
|
||||
if (FLAG_pretenuring_call_new &&
|
||||
isolate()->heap()->ShouldGloballyPretenure()) {
|
||||
flags = static_cast<HAllocate::Flags>(
|
||||
flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE);
|
||||
}
|
||||
|
||||
HInstruction* receiver =
|
||||
AddInstruction(new(zone()) HAllocate(context,
|
||||
size_in_bytes,
|
||||
HType::JSObject(),
|
||||
flags));
|
||||
HAllocate::cast(receiver)->set_known_initial_map(initial_map);
|
||||
|
||||
// Load the initial map from the constructor.
|
||||
HValue* constructor_value =
|
||||
AddInstruction(new(zone()) HConstant(constructor));
|
||||
HValue* initial_map_value =
|
||||
AddLoad(constructor_value, HObjectAccess::ForJSObjectOffset(
|
||||
JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
// Initialize map and fields of the newly allocated object.
|
||||
{ NoObservableSideEffectsScope no_effects(this);
|
||||
ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
|
||||
AddStore(receiver,
|
||||
HObjectAccess::ForJSObjectOffset(JSObject::kMapOffset),
|
||||
initial_map_value);
|
||||
HValue* empty_fixed_array =
|
||||
AddInstruction(new(zone()) HConstant(factory->empty_fixed_array()));
|
||||
AddStore(receiver,
|
||||
HObjectAccess::ForJSObjectOffset(JSObject::kPropertiesOffset),
|
||||
empty_fixed_array);
|
||||
AddStore(receiver,
|
||||
HObjectAccess::ForJSObjectOffset(JSObject::kElementsOffset),
|
||||
empty_fixed_array);
|
||||
if (initial_map->inobject_properties() != 0) {
|
||||
HConstant* undefined = graph()->GetConstantUndefined();
|
||||
for (int i = 0; i < initial_map->inobject_properties(); i++) {
|
||||
int property_offset = JSObject::kHeaderSize + i * kPointerSize;
|
||||
AddStore(receiver,
|
||||
HObjectAccess::ForJSObjectOffset(property_offset),
|
||||
undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the constructor function with a newly allocated receiver using
|
||||
// the index of the receiver from the top of the expression stack.
|
||||
// Replace the constructor function with a newly allocated receiver.
|
||||
HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
|
||||
// Index of the receiver from the top of the expression stack.
|
||||
const int receiver_index = argument_count - 1;
|
||||
AddInstruction(receiver);
|
||||
ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
|
||||
environment()->SetExpressionStackAt(receiver_index, receiver);
|
||||
|
||||
if (TryInlineConstruct(expr, receiver)) return;
|
||||
|
||||
// TODO(mstarzinger): For now we remove the previous HAllocate and all
|
||||
// corresponding instructions and instead add HPushArgument for the
|
||||
// arguments in case inlining failed. What we actually should do is for
|
||||
// inlining to try to build a subgraph without mutating the parent graph.
|
||||
HInstruction* instr = current_block()->last();
|
||||
while (instr != initial_map_value) {
|
||||
HInstruction* prev_instr = instr->previous();
|
||||
instr->DeleteAndReplaceWith(NULL);
|
||||
instr = prev_instr;
|
||||
}
|
||||
initial_map_value->DeleteAndReplaceWith(NULL);
|
||||
// TODO(mstarzinger): For now we remove the previous HAllocateObject and
|
||||
// add HPushArgument for the arguments in case inlining failed. What we
|
||||
// actually should do is emit HInvokeFunction on the constructor instead
|
||||
// of using HCallNew as a fallback.
|
||||
receiver->DeleteAndReplaceWith(NULL);
|
||||
check->DeleteAndReplaceWith(NULL);
|
||||
environment()->SetExpressionStackAt(receiver_index, function);
|
||||
|
@ -66,6 +66,7 @@ class HBasicBlock: public ZoneObject {
|
||||
HInstruction* first() const { return first_; }
|
||||
HInstruction* last() const { return last_; }
|
||||
void set_last(HInstruction* instr) { last_ = instr; }
|
||||
HInstruction* GetLastInstruction();
|
||||
HControlInstruction* end() const { return end_; }
|
||||
HLoopInformation* loop_information() const { return loop_information_; }
|
||||
const ZoneList<HBasicBlock*>* predecessors() const { return &predecessors_; }
|
||||
|
@ -5991,6 +5991,95 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred =
|
||||
new(zone()) DeferredAllocateObject(this, instr);
|
||||
|
||||
Register result = ToRegister(instr->result());
|
||||
Register scratch = ToRegister(instr->temp());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
ASSERT(initial_map->pre_allocated_property_fields() +
|
||||
initial_map->unused_property_fields() -
|
||||
initial_map->inobject_properties() == 0);
|
||||
|
||||
__ Allocate(instance_size, result, no_reg, scratch, deferred->entry(),
|
||||
TAG_OBJECT);
|
||||
|
||||
__ bind(deferred->exit());
|
||||
if (FLAG_debug_code) {
|
||||
Label is_in_new_space;
|
||||
__ JumpIfInNewSpace(result, scratch, &is_in_new_space);
|
||||
__ Abort("Allocated object is not in new-space");
|
||||
__ bind(&is_in_new_space);
|
||||
}
|
||||
|
||||
// Load the initial map.
|
||||
Register map = scratch;
|
||||
__ LoadHeapObject(scratch, constructor);
|
||||
__ mov(map, FieldOperand(scratch, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ AssertNotSmi(map);
|
||||
__ cmpb(FieldOperand(map, Map::kInstanceSizeOffset),
|
||||
instance_size >> kPointerSizeLog2);
|
||||
__ Assert(equal, "Unexpected instance size");
|
||||
__ cmpb(FieldOperand(map, Map::kPreAllocatedPropertyFieldsOffset),
|
||||
initial_map->pre_allocated_property_fields());
|
||||
__ Assert(equal, "Unexpected pre-allocated property fields count");
|
||||
__ cmpb(FieldOperand(map, Map::kUnusedPropertyFieldsOffset),
|
||||
initial_map->unused_property_fields());
|
||||
__ Assert(equal, "Unexpected unused property fields count");
|
||||
__ cmpb(FieldOperand(map, Map::kInObjectPropertiesOffset),
|
||||
initial_map->inobject_properties());
|
||||
__ Assert(equal, "Unexpected in-object property fields count");
|
||||
}
|
||||
|
||||
// Initialize map and fields of the newly allocated object.
|
||||
ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
|
||||
__ mov(FieldOperand(result, JSObject::kMapOffset), map);
|
||||
__ mov(scratch, factory()->empty_fixed_array());
|
||||
__ mov(FieldOperand(result, JSObject::kElementsOffset), scratch);
|
||||
__ mov(FieldOperand(result, JSObject::kPropertiesOffset), scratch);
|
||||
if (initial_map->inobject_properties() != 0) {
|
||||
__ mov(scratch, factory()->undefined_value());
|
||||
for (int i = 0; i < initial_map->inobject_properties(); i++) {
|
||||
int property_offset = JSObject::kHeaderSize + i * kPointerSize;
|
||||
__ mov(FieldOperand(result, property_offset), scratch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ Set(result, Immediate(0));
|
||||
|
||||
PushSafepointRegistersScope scope(this);
|
||||
__ push(Immediate(Smi::FromInt(instance_size)));
|
||||
CallRuntimeFromDeferred(
|
||||
Runtime::kAllocateInNewSpace, 1, instr, instr->context());
|
||||
__ StoreToSafepointRegisterSlot(result, eax);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocate(LAllocate* instr) {
|
||||
class DeferredAllocate: public LDeferredCode {
|
||||
public:
|
||||
|
@ -157,6 +157,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredRandom(LRandom* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredAllocate(LAllocate* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
@ -2548,6 +2548,15 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LOperand* context = UseAny(instr->context());
|
||||
LOperand* temp = TempRegister();
|
||||
LAllocateObject* result = new(zone()) LAllocateObject(context, temp);
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LOperand* context = UseAny(instr->context());
|
||||
|
@ -44,6 +44,7 @@ class LCodeGen;
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(Allocate) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -2551,6 +2552,21 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 1, 1> {
|
||||
public:
|
||||
LAllocateObject(LOperand* context, LOperand* temp) {
|
||||
inputs_[0] = context;
|
||||
temps_[0] = temp;
|
||||
}
|
||||
|
||||
LOperand* context() { return inputs_[0]; }
|
||||
LOperand* temp() { return temps_[0]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LAllocate: public LTemplateInstruction<1, 2, 1> {
|
||||
public:
|
||||
LAllocate(LOperand* context, LOperand* size, LOperand* temp) {
|
||||
|
@ -5293,6 +5293,80 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred =
|
||||
new(zone()) DeferredAllocateObject(this, instr);
|
||||
|
||||
Register result = ToRegister(instr->result());
|
||||
Register scratch = ToRegister(instr->temp());
|
||||
Register scratch2 = ToRegister(instr->temp2());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
ASSERT(initial_map->pre_allocated_property_fields() +
|
||||
initial_map->unused_property_fields() -
|
||||
initial_map->inobject_properties() == 0);
|
||||
|
||||
__ Allocate(instance_size, result, scratch, scratch2, deferred->entry(),
|
||||
TAG_OBJECT);
|
||||
|
||||
__ bind(deferred->exit());
|
||||
if (FLAG_debug_code) {
|
||||
Label is_in_new_space;
|
||||
__ JumpIfInNewSpace(result, scratch, &is_in_new_space);
|
||||
__ Abort("Allocated object is not in new-space");
|
||||
__ bind(&is_in_new_space);
|
||||
}
|
||||
|
||||
// Load the initial map.
|
||||
Register map = scratch;
|
||||
__ LoadHeapObject(map, constructor);
|
||||
__ lw(map, FieldMemOperand(map, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
// Initialize map and fields of the newly allocated object.
|
||||
ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
|
||||
__ sw(map, FieldMemOperand(result, JSObject::kMapOffset));
|
||||
__ LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ sw(scratch, FieldMemOperand(result, JSObject::kElementsOffset));
|
||||
__ sw(scratch, FieldMemOperand(result, JSObject::kPropertiesOffset));
|
||||
if (initial_map->inobject_properties() != 0) {
|
||||
__ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
|
||||
for (int i = 0; i < initial_map->inobject_properties(); i++) {
|
||||
int property_offset = JSObject::kHeaderSize + i * kPointerSize;
|
||||
__ sw(scratch, FieldMemOperand(result, property_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ mov(result, zero_reg);
|
||||
|
||||
PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
|
||||
__ li(a0, Operand(Smi::FromInt(instance_size)));
|
||||
__ push(a0);
|
||||
CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
|
||||
__ StoreToSafepointRegisterSlot(v0, result);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocate(LAllocate* instr) {
|
||||
class DeferredAllocate: public LDeferredCode {
|
||||
public:
|
||||
|
@ -149,6 +149,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredRandom(LRandom* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredAllocate(LAllocate* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
@ -2338,6 +2338,14 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LAllocateObject* result =
|
||||
new(zone()) LAllocateObject(TempRegister(), TempRegister());
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LOperand* size = instr->size()->IsConstant()
|
||||
|
@ -49,6 +49,7 @@ class LCodeGen;
|
||||
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(AllocateObject) \
|
||||
V(Allocate) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
@ -2417,6 +2418,21 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 1, 2> {
|
||||
public:
|
||||
LAllocateObject(LOperand* temp, LOperand* temp2) {
|
||||
temps_[0] = temp;
|
||||
temps_[1] = temp2;
|
||||
}
|
||||
|
||||
LOperand* temp() { return temps_[0]; }
|
||||
LOperand* temp2() { return temps_[1]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LAllocate: public LTemplateInstruction<1, 2, 2> {
|
||||
public:
|
||||
LAllocate(LOperand* size, LOperand* temp1, LOperand* temp2) {
|
||||
|
@ -5476,13 +5476,6 @@ class Map: public HeapObject {
|
||||
int NumberOfDescribedProperties(DescriptorFlag which = OWN_DESCRIPTORS,
|
||||
PropertyAttributes filter = NONE);
|
||||
|
||||
// Returns the number of slots allocated for the initial properties
|
||||
// backing storage for instances of this map.
|
||||
int InitialPropertiesLength() {
|
||||
return pre_allocated_property_fields() + unused_property_fields() -
|
||||
inobject_properties();
|
||||
}
|
||||
|
||||
// Casting.
|
||||
static inline Map* cast(Object* obj);
|
||||
|
||||
|
@ -5084,6 +5084,94 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
|
||||
class DeferredAllocateObject: public LDeferredCode {
|
||||
public:
|
||||
DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
|
||||
: LDeferredCode(codegen), instr_(instr) { }
|
||||
virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
|
||||
virtual LInstruction* instr() { return instr_; }
|
||||
private:
|
||||
LAllocateObject* instr_;
|
||||
};
|
||||
|
||||
DeferredAllocateObject* deferred =
|
||||
new(zone()) DeferredAllocateObject(this, instr);
|
||||
|
||||
Register result = ToRegister(instr->result());
|
||||
Register scratch = ToRegister(instr->temp());
|
||||
Handle<JSFunction> constructor = instr->hydrogen()->constructor();
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
ASSERT(initial_map->pre_allocated_property_fields() +
|
||||
initial_map->unused_property_fields() -
|
||||
initial_map->inobject_properties() == 0);
|
||||
|
||||
__ Allocate(instance_size, result, no_reg, scratch, deferred->entry(),
|
||||
TAG_OBJECT);
|
||||
|
||||
__ bind(deferred->exit());
|
||||
if (FLAG_debug_code) {
|
||||
Label is_in_new_space;
|
||||
__ JumpIfInNewSpace(result, scratch, &is_in_new_space);
|
||||
__ Abort("Allocated object is not in new-space");
|
||||
__ bind(&is_in_new_space);
|
||||
}
|
||||
|
||||
// Load the initial map.
|
||||
Register map = scratch;
|
||||
__ LoadHeapObject(scratch, constructor);
|
||||
__ movq(map, FieldOperand(scratch, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ AssertNotSmi(map);
|
||||
__ cmpb(FieldOperand(map, Map::kInstanceSizeOffset),
|
||||
Immediate(instance_size >> kPointerSizeLog2));
|
||||
__ Assert(equal, "Unexpected instance size");
|
||||
__ cmpb(FieldOperand(map, Map::kPreAllocatedPropertyFieldsOffset),
|
||||
Immediate(initial_map->pre_allocated_property_fields()));
|
||||
__ Assert(equal, "Unexpected pre-allocated property fields count");
|
||||
__ cmpb(FieldOperand(map, Map::kUnusedPropertyFieldsOffset),
|
||||
Immediate(initial_map->unused_property_fields()));
|
||||
__ Assert(equal, "Unexpected unused property fields count");
|
||||
__ cmpb(FieldOperand(map, Map::kInObjectPropertiesOffset),
|
||||
Immediate(initial_map->inobject_properties()));
|
||||
__ Assert(equal, "Unexpected in-object property fields count");
|
||||
}
|
||||
|
||||
// Initialize map and fields of the newly allocated object.
|
||||
ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
|
||||
__ movq(FieldOperand(result, JSObject::kMapOffset), map);
|
||||
__ LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex);
|
||||
__ movq(FieldOperand(result, JSObject::kElementsOffset), scratch);
|
||||
__ movq(FieldOperand(result, JSObject::kPropertiesOffset), scratch);
|
||||
if (initial_map->inobject_properties() != 0) {
|
||||
__ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
|
||||
for (int i = 0; i < initial_map->inobject_properties(); i++) {
|
||||
int property_offset = JSObject::kHeaderSize + i * kPointerSize;
|
||||
__ movq(FieldOperand(result, property_offset), scratch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Handle<Map> initial_map = instr->hydrogen()->constructor_initial_map();
|
||||
int instance_size = initial_map->instance_size();
|
||||
|
||||
// TODO(3095996): Get rid of this. For now, we need to make the
|
||||
// result register contain a valid pointer because it is already
|
||||
// contained in the register pointer map.
|
||||
__ Set(result, 0);
|
||||
|
||||
PushSafepointRegistersScope scope(this);
|
||||
__ Push(Smi::FromInt(instance_size));
|
||||
CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
|
||||
__ StoreToSafepointRegisterSlot(result, rax);
|
||||
}
|
||||
|
||||
|
||||
void LCodeGen::DoAllocate(LAllocate* instr) {
|
||||
class DeferredAllocate: public LDeferredCode {
|
||||
public:
|
||||
|
@ -128,6 +128,7 @@ class LCodeGen BASE_EMBEDDED {
|
||||
void DoDeferredRandom(LRandom* instr);
|
||||
void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
|
||||
void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
|
||||
void DoDeferredAllocateObject(LAllocateObject* instr);
|
||||
void DoDeferredAllocate(LAllocate* instr);
|
||||
void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
|
||||
Label* map_check);
|
||||
|
@ -2357,6 +2357,13 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LAllocateObject* result = new(zone()) LAllocateObject(TempRegister());
|
||||
return AssignPointerMap(DefineAsRegister(result));
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
|
||||
info()->MarkAsDeferredCalling();
|
||||
LOperand* size = instr->size()->IsConstant()
|
||||
|
@ -50,6 +50,7 @@ class LCodeGen;
|
||||
V(AccessArgumentsAt) \
|
||||
V(AddI) \
|
||||
V(Allocate) \
|
||||
V(AllocateObject) \
|
||||
V(ApplyArguments) \
|
||||
V(ArgumentsElements) \
|
||||
V(ArgumentsLength) \
|
||||
@ -2362,6 +2363,19 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LAllocateObject: public LTemplateInstruction<1, 0, 1> {
|
||||
public:
|
||||
explicit LAllocateObject(LOperand* temp) {
|
||||
temps_[0] = temp;
|
||||
}
|
||||
|
||||
LOperand* temp() { return temps_[0]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
|
||||
DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
|
||||
};
|
||||
|
||||
|
||||
class LAllocate: public LTemplateInstruction<1, 1, 1> {
|
||||
public:
|
||||
LAllocate(LOperand* size, LOperand* temp) {
|
||||
|
@ -2188,30 +2188,6 @@ TEST(OptimizedAllocationArrayLiterals) {
|
||||
}
|
||||
|
||||
|
||||
TEST(OptimizedPretenuringCallNew) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
i::FLAG_pretenuring_call_new = true;
|
||||
CcTest::InitializeVM();
|
||||
if (!i::V8::UseCrankshaft() || i::FLAG_always_opt) return;
|
||||
if (i::FLAG_gc_global || i::FLAG_stress_compaction) return;
|
||||
v8::HandleScope scope(CcTest::isolate());
|
||||
HEAP->SetNewSpaceHighPromotionModeActive(true);
|
||||
|
||||
AlwaysAllocateScope always_allocate;
|
||||
v8::Local<v8::Value> res = CompileRun(
|
||||
"function g() { this.a = 0; }"
|
||||
"function f() {"
|
||||
" return new g();"
|
||||
"};"
|
||||
"f(); f(); f();"
|
||||
"%OptimizeFunctionOnNextCall(f);"
|
||||
"f();");
|
||||
|
||||
Handle<JSObject> o =
|
||||
v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
|
||||
CHECK(HEAP->InOldPointerSpace(*o));
|
||||
}
|
||||
|
||||
static int CountMapTransitions(Map* map) {
|
||||
return map->transitions()->number_of_transitions();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user