Implement crankshaft support for nested object literals.

This generates optimized code for deep-copying of nested object literal
boilerplates which are statically known. Most of the boilerplates have
already been generated at crankshaft time, so this optimization should
kick in for virtually every object literal. Only nested object literal
graphs up to a certain depth and containing up to a certain total number
of properties are considered for this optimization. This will prevent
explosion of code size due to large object literals (e.g. eval on JSON).
Improves splay performance because object literals are created often.

R=fschneider@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10061 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mstarzinger@chromium.org 2011-11-24 13:42:52 +00:00
parent 4c766c2330
commit 7e7d4dfd64
15 changed files with 419 additions and 41 deletions

View File

@ -2070,8 +2070,14 @@ LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
}
LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) {
return MarkAsCall(DefineFixed(new LObjectLiteral, r0), instr);
LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) {
return MarkAsCall(DefineFixed(new LObjectLiteralFast, r0), instr);
}
LInstruction* LChunkBuilder::DoObjectLiteralGeneric(
HObjectLiteralGeneric* instr) {
return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, r0), instr);
}

View File

@ -134,7 +134,8 @@ class LCodeGen;
V(NumberTagD) \
V(NumberTagI) \
V(NumberUntagD) \
V(ObjectLiteral) \
V(ObjectLiteralFast) \
V(ObjectLiteralGeneric) \
V(OsrEntry) \
V(OuterContext) \
V(Parameter) \
@ -1900,10 +1901,17 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> {
};
class LObjectLiteral: public LTemplateInstruction<1, 0, 0> {
class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral)
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast)
};
class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric)
};

View File

@ -4254,7 +4254,74 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
}
void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
int* offset) {
ASSERT(!source.is(r2));
ASSERT(!result.is(r2));
// Increase the offset so that subsequent objects end up right after
// this one.
int current_offset = *offset;
int size = object->map()->instance_size();
*offset += size;
// Copy object header.
ASSERT(object->properties()->length() == 0);
ASSERT(object->elements()->length() == 0 ||
object->elements()->map() == isolate()->heap()->fixed_cow_array_map());
int inobject_properties = object->map()->inobject_properties();
int header_size = size - inobject_properties * kPointerSize;
for (int i = 0; i < header_size; i += kPointerSize) {
__ ldr(r2, FieldMemOperand(source, i));
__ str(r2, FieldMemOperand(result, current_offset + i));
}
// Copy in-object properties.
for (int i = 0; i < inobject_properties; i++) {
int total_offset = current_offset + object->GetInObjectPropertyOffset(i);
Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i));
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
__ add(r2, result, Operand(*offset));
__ str(r2, FieldMemOperand(result, total_offset));
LoadHeapObject(source, value_object);
EmitDeepCopy(value_object, result, source, offset);
} else if (value->IsHeapObject()) {
LoadHeapObject(r2, Handle<HeapObject>::cast(value));
__ str(r2, FieldMemOperand(result, total_offset));
} else {
__ mov(r2, Operand(value));
__ str(r2, FieldMemOperand(result, total_offset));
}
}
}
void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) {
int size = instr->hydrogen()->total_size();
// Allocate all objects that are part of the literal in one big
// allocation. This avoids multiple limit checks.
Label allocated, runtime_allocate;
__ AllocateInNewSpace(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT);
__ jmp(&allocated);
__ bind(&runtime_allocate);
__ mov(r0, Operand(Smi::FromInt(size)));
__ push(r0);
CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
__ bind(&allocated);
int offset = 0;
LoadHeapObject(r1, instr->hydrogen()->boilerplate());
EmitDeepCopy(instr->hydrogen()->boilerplate(), r0, r1, &offset);
ASSERT_EQ(size, offset);
}
void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) {
Handle<FixedArray> constant_properties =
instr->hydrogen()->constant_properties();

View File

@ -300,6 +300,13 @@ class LCodeGen BASE_EMBEDDED {
Handle<Map> type,
Handle<String> name);
// Emits optimized code to deep-copy the contents of statically known
// object graphs (e.g. object literal boilerplate).
void EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
int* offset);
struct JumpTableEntry {
explicit inline JumpTableEntry(Address entry)
: label(),

View File

@ -1792,7 +1792,12 @@ HType HArrayLiteral::CalculateInferredType() {
}
HType HObjectLiteral::CalculateInferredType() {
HType HObjectLiteralFast::CalculateInferredType() {
return HType::JSObject();
}
HType HObjectLiteralGeneric::CalculateInferredType() {
return HType::JSObject();
}

View File

@ -121,7 +121,7 @@ class LChunkBuilder;
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(StringCompareAndBranch) \
V(StringCompareAndBranch) \
V(JSArrayLength) \
V(LeaveInlined) \
V(LoadContextSlot) \
@ -139,7 +139,8 @@ class LChunkBuilder;
V(LoadNamedGeneric) \
V(Mod) \
V(Mul) \
V(ObjectLiteral) \
V(ObjectLiteralFast) \
V(ObjectLiteralGeneric) \
V(OsrEntry) \
V(OuterContext) \
V(Parameter) \
@ -4195,14 +4196,49 @@ class HArrayLiteral: public HMaterializedLiteral<1> {
};
class HObjectLiteral: public HMaterializedLiteral<1> {
class HObjectLiteralFast: public HMaterializedLiteral<1> {
public:
HObjectLiteral(HValue* context,
Handle<FixedArray> constant_properties,
bool fast_elements,
int literal_index,
int depth,
bool has_function)
HObjectLiteralFast(HValue* context,
Handle<JSObject> boilerplate,
int total_size,
int literal_index,
int depth)
: HMaterializedLiteral<1>(literal_index, depth),
boilerplate_(boilerplate),
total_size_(total_size) {
SetOperandAt(0, context);
}
// Maximum depth and total number of properties for object literal
// graphs to be considered for fast deep-copying.
static const int kMaxObjectLiteralDepth = 3;
static const int kMaxObjectLiteralProperties = 8;
HValue* context() { return OperandAt(0); }
Handle<JSObject> boilerplate() const { return boilerplate_; }
int total_size() const { return total_size_; }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
}
virtual HType CalculateInferredType();
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast)
private:
Handle<JSObject> boilerplate_;
int total_size_;
};
class HObjectLiteralGeneric: public HMaterializedLiteral<1> {
public:
HObjectLiteralGeneric(HValue* context,
Handle<FixedArray> constant_properties,
bool fast_elements,
int literal_index,
int depth,
bool has_function)
: HMaterializedLiteral<1>(literal_index, depth),
constant_properties_(constant_properties),
fast_elements_(fast_elements),
@ -4222,7 +4258,7 @@ class HObjectLiteral: public HMaterializedLiteral<1> {
}
virtual HType CalculateInferredType();
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral)
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric)
private:
Handle<FixedArray> constant_properties_;
@ -4317,7 +4353,7 @@ class HToFastProperties: public HUnaryOperation {
// This instruction is not marked as having side effects, but
// changes the map of the input operand. Use it only when creating
// object literals.
ASSERT(value->IsObjectLiteral());
ASSERT(value->IsObjectLiteralGeneric());
set_representation(Representation::Tagged());
}

View File

@ -3316,18 +3316,78 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
}
// Determines whether the given object literal boilerplate satisfies all
// limits to be considered for fast deep-copying and computes the total
// size of all objects that are part of the graph.
static bool IsFastObjectLiteral(Handle<JSObject> boilerplate,
int max_depth,
int* max_properties,
int* total_size) {
if (max_depth <= 0) return false;
FixedArrayBase* elements = boilerplate->elements();
if (elements->length() > 0 &&
elements->map() != HEAP->fixed_cow_array_map()) {
return false;
}
FixedArray* properties = boilerplate->properties();
if (properties->length() > 0) {
return false;
} else {
int nof = boilerplate->map()->inobject_properties();
for (int i = 0; i < nof; i++) {
if ((*max_properties)-- <= 0) return false;
Handle<Object> value(boilerplate->InObjectPropertyAt(i));
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
if (!IsFastObjectLiteral(value_object,
max_depth - 1,
max_properties,
total_size)) {
return false;
}
}
}
}
*total_size += boilerplate->map()->instance_size();
return true;
}
void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
Handle<JSFunction> closure = function_state()->compilation_info()->closure();
HValue* context = environment()->LookupContext();
HObjectLiteral* literal =
new(zone()) HObjectLiteral(context,
expr->constant_properties(),
expr->fast_elements(),
expr->literal_index(),
expr->depth(),
expr->has_function());
HInstruction* literal;
// Check whether to use fast or slow deep-copying for boilerplate.
int total_size = 0;
int max_properties = HObjectLiteralFast::kMaxObjectLiteralProperties;
Handle<Object> boilerplate(closure->literals()->get(expr->literal_index()));
if (boilerplate->IsJSObject() &&
IsFastObjectLiteral(Handle<JSObject>::cast(boilerplate),
HObjectLiteralFast::kMaxObjectLiteralDepth,
&max_properties,
&total_size)) {
Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate);
literal = new(zone()) HObjectLiteralFast(context,
boilerplate_object,
total_size,
expr->literal_index(),
expr->depth());
} else {
literal = new(zone()) HObjectLiteralGeneric(context,
expr->constant_properties(),
expr->fast_elements(),
expr->literal_index(),
expr->depth(),
expr->has_function());
}
// The object is expected in the bailout environment during computation
// of the property values and is the value of the entire expression.
PushAndAdd(literal);

View File

@ -4161,7 +4161,79 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
}
void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
int* offset) {
ASSERT(!source.is(ecx));
ASSERT(!result.is(ecx));
if (FLAG_debug_code) {
LoadHeapObject(ecx, object);
__ cmp(source, ecx);
__ Assert(equal, "Unexpected object literal boilerplate");
}
// Increase the offset so that subsequent objects end up right after
// this one.
int current_offset = *offset;
int size = object->map()->instance_size();
*offset += size;
// Copy object header.
ASSERT(object->properties()->length() == 0);
ASSERT(object->elements()->length() == 0 ||
object->elements()->map() == isolate()->heap()->fixed_cow_array_map());
int inobject_properties = object->map()->inobject_properties();
int header_size = size - inobject_properties * kPointerSize;
for (int i = 0; i < header_size; i += kPointerSize) {
__ mov(ecx, FieldOperand(source, i));
__ mov(FieldOperand(result, current_offset + i), ecx);
}
// Copy in-object properties.
for (int i = 0; i < inobject_properties; i++) {
int total_offset = current_offset + object->GetInObjectPropertyOffset(i);
Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i));
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
__ lea(ecx, Operand(result, *offset));
__ mov(FieldOperand(result, total_offset), ecx);
LoadHeapObject(source, value_object);
EmitDeepCopy(value_object, result, source, offset);
} else if (value->IsHeapObject()) {
LoadHeapObject(ecx, Handle<HeapObject>::cast(value));
__ mov(FieldOperand(result, total_offset), ecx);
} else {
__ mov(FieldOperand(result, total_offset), Immediate(value));
}
}
}
void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
int size = instr->hydrogen()->total_size();
// Allocate all objects that are part of the literal in one big
// allocation. This avoids multiple limit checks.
Label allocated, runtime_allocate;
__ AllocateInNewSpace(size, eax, ecx, edx, &runtime_allocate, TAG_OBJECT);
__ jmp(&allocated);
__ bind(&runtime_allocate);
__ push(Immediate(Smi::FromInt(size)));
CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
__ bind(&allocated);
int offset = 0;
LoadHeapObject(ebx, instr->hydrogen()->boilerplate());
EmitDeepCopy(instr->hydrogen()->boilerplate(), eax, ebx, &offset);
ASSERT_EQ(size, offset);
}
void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
Handle<FixedArray> constant_properties =
instr->hydrogen()->constant_properties();

View File

@ -295,6 +295,14 @@ class LCodeGen BASE_EMBEDDED {
Register object,
Handle<Map> type,
Handle<String> name);
// Emits optimized code to deep-copy the contents of statically known
// object graphs (e.g. object literal boilerplate).
void EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
int* offset);
void EnsureSpaceForLazyDeopt();
LChunk* const chunk_;

View File

@ -2181,10 +2181,18 @@ LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
}
LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) {
LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) {
LOperand* context = UseFixed(instr->context(), esi);
return MarkAsCall(
DefineFixed(new(zone()) LObjectLiteral(context), eax), instr);
DefineFixed(new(zone()) LObjectLiteralFast(context), eax), instr);
}
LInstruction* LChunkBuilder::DoObjectLiteralGeneric(
HObjectLiteralGeneric* instr) {
LOperand* context = UseFixed(instr->context(), esi);
return MarkAsCall(
DefineFixed(new(zone()) LObjectLiteralGeneric(context), eax), instr);
}

View File

@ -128,7 +128,8 @@ class LCodeGen;
V(NumberTagD) \
V(NumberTagI) \
V(NumberUntagD) \
V(ObjectLiteral) \
V(ObjectLiteralFast) \
V(ObjectLiteralGeneric) \
V(OsrEntry) \
V(OuterContext) \
V(Parameter) \
@ -1960,16 +1961,29 @@ class LArrayLiteral: public LTemplateInstruction<1, 1, 0> {
};
class LObjectLiteral: public LTemplateInstruction<1, 1, 0> {
class LObjectLiteralFast: public LTemplateInstruction<1, 1, 0> {
public:
explicit LObjectLiteral(LOperand* context) {
explicit LObjectLiteralFast(LOperand* context) {
inputs_[0] = context;
}
LOperand* context() { return inputs_[0]; }
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral)
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast)
};
class LObjectLiteralGeneric: public LTemplateInstruction<1, 1, 0> {
public:
explicit LObjectLiteralGeneric(LOperand* context) {
inputs_[0] = context;
}
LOperand* context() { return inputs_[0]; }
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric)
};

View File

@ -3856,7 +3856,73 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
}
void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
int* offset) {
ASSERT(!source.is(rcx));
ASSERT(!result.is(rcx));
// Increase the offset so that subsequent objects end up right after
// this one.
int current_offset = *offset;
int size = object->map()->instance_size();
*offset += size;
// Copy object header.
ASSERT(object->properties()->length() == 0);
ASSERT(object->elements()->length() == 0 ||
object->elements()->map() == isolate()->heap()->fixed_cow_array_map());
int inobject_properties = object->map()->inobject_properties();
int header_size = size - inobject_properties * kPointerSize;
for (int i = 0; i < header_size; i += kPointerSize) {
__ movq(rcx, FieldOperand(source, i));
__ movq(FieldOperand(result, current_offset + i), rcx);
}
// Copy in-object properties.
for (int i = 0; i < inobject_properties; i++) {
int total_offset = current_offset + object->GetInObjectPropertyOffset(i);
Handle<Object> value = Handle<Object>(object->InObjectPropertyAt(i));
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
__ lea(rcx, Operand(result, *offset));
__ movq(FieldOperand(result, total_offset), rcx);
LoadHeapObject(source, value_object);
EmitDeepCopy(value_object, result, source, offset);
} else if (value->IsHeapObject()) {
LoadHeapObject(rcx, Handle<HeapObject>::cast(value));
__ movq(FieldOperand(result, total_offset), rcx);
} else {
__ movq(rcx, value, RelocInfo::NONE);
__ movq(FieldOperand(result, total_offset), rcx);
}
}
}
void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) {
int size = instr->hydrogen()->total_size();
// Allocate all objects that are part of the literal in one big
// allocation. This avoids multiple limit checks.
Label allocated, runtime_allocate;
__ AllocateInNewSpace(size, rax, rcx, rdx, &runtime_allocate, TAG_OBJECT);
__ jmp(&allocated);
__ bind(&runtime_allocate);
__ Push(Smi::FromInt(size));
CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
__ bind(&allocated);
int offset = 0;
LoadHeapObject(rbx, instr->hydrogen()->boilerplate());
EmitDeepCopy(instr->hydrogen()->boilerplate(), rax, rbx, &offset);
ASSERT_EQ(size, offset);
}
void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) {
Handle<FixedArray> constant_properties =
instr->hydrogen()->constant_properties();

View File

@ -289,6 +289,13 @@ class LCodeGen BASE_EMBEDDED {
// register, or a stack slot operand.
void EmitPushTaggedOperand(LOperand* operand);
// Emits optimized code to deep-copy the contents of statically known
// object graphs (e.g. object literal boilerplate).
void EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
int* offset);
struct JumpTableEntry {
explicit inline JumpTableEntry(Address entry)
: label(),

View File

@ -2071,8 +2071,14 @@ LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
}
LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) {
return MarkAsCall(DefineFixed(new LObjectLiteral, rax), instr);
LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) {
return MarkAsCall(DefineFixed(new LObjectLiteralFast, rax), instr);
}
LInstruction* LChunkBuilder::DoObjectLiteralGeneric(
HObjectLiteralGeneric* instr) {
return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, rax), instr);
}

View File

@ -134,7 +134,8 @@ class LCodeGen;
V(NumberTagD) \
V(NumberTagI) \
V(NumberUntagD) \
V(ObjectLiteral) \
V(ObjectLiteralFast) \
V(ObjectLiteralGeneric) \
V(OsrEntry) \
V(OuterContext) \
V(Parameter) \
@ -1890,10 +1891,17 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> {
};
class LObjectLiteral: public LTemplateInstruction<1, 0, 0> {
class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral)
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast)
};
class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic")
DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric)
};