Revision: 10138
Author: [email protected]
Date: Fri Dec 2 04:42:35 2011
Log: Optimize Crankshaft array literal initialization from boilerplate.
BUG=none
TEST=test/mjsunit/array-literal-transitions.js
Review URL: http://codereview.chromium.org/8747009
http://code.google.com/p/v8/source/detail?r=10138
Modified:
/branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
/branches/bleeding_edge/src/hydrogen-instructions.cc
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/array-literal-transitions.js
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Thu Dec 1
08:57:54 2011
+++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Fri Dec 2
04:42:35 2011
@@ -4231,15 +4231,31 @@
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
- Handle<FixedArray> constant_elements =
instr->hydrogen()->constant_elements();
- ASSERT_EQ(2, constant_elements->length());
- ElementsKind constant_elements_kind =
-
static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
+ Heap* heap = isolate()->heap();
+ ElementsKind boilerplate_elements_kind =
+ instr->hydrogen()->boilerplate_elements_kind();
+
+ // Deopt if the array literal boilerplate ElementsKind is of a type
different
+ // than the expected one. The check isn't necessary if the boilerplate
has
+ // already been converted to FAST_ELEMENTS.
+ if (boilerplate_elements_kind != FAST_ELEMENTS) {
+ LoadHeapObject(r1, instr->hydrogen()->boilerplate_object());
+ // Load map into r2.
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+ // Load the map's "bit field 2".
+ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ ubfx(r2, r2, Map::kElementsKindShift, Map::kElementsKindBitCount);
+ __ cmp(r2, Operand(boilerplate_elements_kind));
+ DeoptimizeIf(ne, instr->environment());
+ }
__ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
__ mov(r2, Operand(Smi::FromInt(instr->hydrogen()->literal_index())));
- __ mov(r1, Operand(constant_elements));
+ // Boilerplate already exists, constant elements are never accessed.
+ // Pass an empty fixed array.
+ __ mov(r1, Operand(Handle<FixedArray>(heap->empty_fixed_array())));
__ Push(r3, r2, r1);
// Pick the right runtime function or stub to call.
@@ -4256,9 +4272,9 @@
CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
} else {
FastCloneShallowArrayStub::Mode mode =
- constant_elements_kind == FAST_DOUBLE_ELEMENTS
- ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
+ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
FastCloneShallowArrayStub stub(mode, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.cc Thu Nov 24
05:42:52 2011
+++ /branches/bleeding_edge/src/hydrogen-instructions.cc Fri Dec 2
04:42:35 2011
@@ -1227,10 +1227,7 @@
bool HArrayLiteral::IsCopyOnWrite() const {
- Handle<FixedArray> constant_elements = this->constant_elements();
- FixedArrayBase* constant_elements_values =
- FixedArrayBase::cast(constant_elements->get(1));
- return constant_elements_values->map() == HEAP->fixed_cow_array_map();
+ return boilerplate_object_->elements()->map() ==
HEAP->fixed_cow_array_map();
}
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Thu Dec 1 08:57:54
2011
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Fri Dec 2 04:42:35
2011
@@ -4167,18 +4167,21 @@
class HArrayLiteral: public HMaterializedLiteral<1> {
public:
HArrayLiteral(HValue* context,
- Handle<FixedArray> constant_elements,
+ Handle<JSObject> boilerplate_object,
int length,
int literal_index,
int depth)
: HMaterializedLiteral<1>(literal_index, depth),
length_(length),
- constant_elements_(constant_elements) {
+ boilerplate_object_(boilerplate_object) {
SetOperandAt(0, context);
}
HValue* context() { return OperandAt(0); }
- Handle<FixedArray> constant_elements() const { return
constant_elements_; }
+ ElementsKind boilerplate_elements_kind() const {
+ return boilerplate_object_->GetElementsKind();
+ }
+ Handle<JSObject> boilerplate_object() const { return
boilerplate_object_; }
int length() const { return length_; }
bool IsCopyOnWrite() const;
@@ -4192,7 +4195,7 @@
private:
int length_;
- Handle<FixedArray> constant_elements_;
+ Handle<JSObject> boilerplate_object_;
};
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Thu Dec 1 08:57:54 2011
+++ /branches/bleeding_edge/src/hydrogen.cc Fri Dec 2 04:42:35 2011
@@ -3457,11 +3457,25 @@
int length = subexprs->length();
HValue* context = environment()->LookupContext();
- HArrayLiteral* literal = new(zone()) HArrayLiteral(context,
-
expr->constant_elements(),
- length,
- expr->literal_index(),
- expr->depth());
+ FixedArray* literals = environment()->closure()->literals();
+ Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
+
+ // For now, no boilerplate causes a deopt.
+ if (raw_boilerplate->IsUndefined()) {
+ AddInstruction(new(zone()) HSoftDeoptimize);
+ return ast_context()->ReturnValue(graph()->GetConstantUndefined());
+ }
+
+ Handle<JSObject> boilerplate(Handle<JSObject>::cast(raw_boilerplate));
+ ElementsKind boilerplate_elements_kind = boilerplate->GetElementsKind();
+
+ HArrayLiteral* literal = new(zone()) HArrayLiteral(
+ context,
+ boilerplate,
+ length,
+ expr->literal_index(),
+ expr->depth());
+
// The array is expected in the bailout environment during computation
// of the property values and is the value of the entire expression.
PushAndAdd(literal);
@@ -3484,42 +3498,25 @@
HValue* key = AddInstruction(
new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
Representation::Integer32()));
- HInstruction* elements_kind =
- AddInstruction(new(zone()) HElementsKind(literal));
- HBasicBlock* store_fast = graph()->CreateBasicBlock();
- // Two empty blocks to satisfy edge split form.
- HBasicBlock* store_fast_edgesplit1 = graph()->CreateBasicBlock();
- HBasicBlock* store_fast_edgesplit2 = graph()->CreateBasicBlock();
- HBasicBlock* store_generic = graph()->CreateBasicBlock();
- HBasicBlock* check_smi_only_elements = graph()->CreateBasicBlock();
- HBasicBlock* join = graph()->CreateBasicBlock();
-
- HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(value);
- smicheck->SetSuccessorAt(0, store_fast_edgesplit1);
- smicheck->SetSuccessorAt(1, check_smi_only_elements);
- current_block()->Finish(smicheck);
- store_fast_edgesplit1->Finish(new(zone()) HGoto(store_fast));
-
- set_current_block(check_smi_only_elements);
- HCompareConstantEqAndBranch* smi_elements_check =
- new(zone()) HCompareConstantEqAndBranch(elements_kind,
- FAST_ELEMENTS,
- Token::EQ_STRICT);
- smi_elements_check->SetSuccessorAt(0, store_fast_edgesplit2);
- smi_elements_check->SetSuccessorAt(1, store_generic);
- current_block()->Finish(smi_elements_check);
- store_fast_edgesplit2->Finish(new(zone()) HGoto(store_fast));
-
- set_current_block(store_fast);
- AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key,
value));
- store_fast->Goto(join);
-
- set_current_block(store_generic);
- AddInstruction(BuildStoreKeyedGeneric(literal, key, value));
- store_generic->Goto(join);
-
- join->SetJoinId(expr->id());
- set_current_block(join);
+
+ switch (boilerplate_elements_kind) {
+ case FAST_SMI_ONLY_ELEMENTS:
+ case FAST_ELEMENTS:
+ AddInstruction(new(zone()) HStoreKeyedFastElement(
+ elements,
+ key,
+ value,
+ boilerplate_elements_kind));
+ break;
+ case FAST_DOUBLE_ELEMENTS:
+ AddInstruction(new(zone()) HStoreKeyedFastDoubleElement(elements,
+ key,
+ value));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
AddSimulate(expr->GetIdForElement(i));
}
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Fri Dec 2
02:01:50 2011
+++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Fri Dec 2
04:42:35 2011
@@ -4107,17 +4107,32 @@
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
-
- Handle<FixedArray> constant_elements =
instr->hydrogen()->constant_elements();
- ASSERT_EQ(2, constant_elements->length());
- ElementsKind constant_elements_kind =
-
static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
+ Heap* heap = isolate()->heap();
+ ElementsKind boilerplate_elements_kind =
+ instr->hydrogen()->boilerplate_elements_kind();
+
+ // Deopt if the array literal boilerplate ElementsKind is of a type
different
+ // than the expected one. The check isn't necessary if the boilerplate
has
+ // already been converted to FAST_ELEMENTS.
+ if (boilerplate_elements_kind != FAST_ELEMENTS) {
+ LoadHeapObject(eax, instr->hydrogen()->boilerplate_object());
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ // Load the map's "bit field 2". We only need the first byte,
+ // but the following masking takes care of that anyway.
+ __ mov(ebx, FieldOperand(ebx, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ and_(ebx, Map::kElementsKindMask);
+ __ cmp(ebx, boilerplate_elements_kind << Map::kElementsKindShift);
+ DeoptimizeIf(not_equal, instr->environment());
+ }
// Setup the parameters to the stub/runtime call.
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ push(FieldOperand(eax, JSFunction::kLiteralsOffset));
__ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index())));
- __ push(Immediate(constant_elements));
+ // Boilerplate already exists, constant elements are never accessed.
+ // Pass an empty fixed array.
+ __ push(Immediate(Handle<FixedArray>(heap->empty_fixed_array())));
// Pick the right runtime function or stub to call.
int length = instr->hydrogen()->length();
@@ -4133,9 +4148,9 @@
CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
} else {
FastCloneShallowArrayStub::Mode mode =
- constant_elements_kind == FAST_DOUBLE_ELEMENTS
- ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
+ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
FastCloneShallowArrayStub stub(mode, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
=======================================
--- /branches/bleeding_edge/src/runtime.cc Tue Nov 29 00:43:14 2011
+++ /branches/bleeding_edge/src/runtime.cc Fri Dec 2 04:42:35 2011
@@ -625,6 +625,7 @@
// Check if boilerplate exists. If not, create it first.
Handle<Object> boilerplate(literals->get(literals_index), isolate);
if (*boilerplate == isolate->heap()->undefined_value()) {
+ ASSERT(*elements != isolate->heap()->empty_fixed_array());
boilerplate = CreateArrayLiteralBoilerplate(isolate, literals,
elements);
if (boilerplate.is_null()) return Failure::Exception();
// Update the functions literal and return the boilerplate.
@@ -4651,6 +4652,7 @@
if (value->IsNumber()) {
ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS);
TransitionElementsKind(object, FAST_DOUBLE_ELEMENTS);
+ TransitionElementsKind(boilerplate_object, FAST_DOUBLE_ELEMENTS);
ASSERT(object->GetElementsKind() == FAST_DOUBLE_ELEMENTS);
FixedDoubleArray* double_array =
FixedDoubleArray::cast(object->elements());
@@ -4660,6 +4662,7 @@
ASSERT(elements_kind == FAST_SMI_ONLY_ELEMENTS ||
elements_kind == FAST_DOUBLE_ELEMENTS);
TransitionElementsKind(object, FAST_ELEMENTS);
+ TransitionElementsKind(boilerplate_object, FAST_ELEMENTS);
FixedArray* object_array =
FixedArray::cast(object->elements());
object_array->set(store_index, *value);
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Thu Dec 1
08:57:54 2011
+++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Fri Dec 2
04:42:35 2011
@@ -3836,16 +3836,32 @@
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
- Handle<FixedArray> constant_elements =
instr->hydrogen()->constant_elements();
- ASSERT_EQ(2, constant_elements->length());
- ElementsKind constant_elements_kind =
-
static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
+ Heap* heap = isolate()->heap();
+ ElementsKind boilerplate_elements_kind =
+ instr->hydrogen()->boilerplate_elements_kind();
+
+ // Deopt if the array literal boilerplate ElementsKind is of a type
different
+ // than the expected one. The check isn't necessary if the boilerplate
has
+ // already been converted to FAST_ELEMENTS.
+ if (boilerplate_elements_kind != FAST_ELEMENTS) {
+ LoadHeapObject(rax, instr->hydrogen()->boilerplate_object());
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ // Load the map's "bit field 2".
+ __ movb(rbx, FieldOperand(rbx, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ and_(rbx, Immediate(Map::kElementsKindMask));
+ __ cmpb(rbx, Immediate(boilerplate_elements_kind <<
+ Map::kElementsKindShift));
+ DeoptimizeIf(not_equal, instr->environment());
+ }
// Setup the parameters to the stub/runtime call.
__ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
__ push(FieldOperand(rax, JSFunction::kLiteralsOffset));
__ Push(Smi::FromInt(instr->hydrogen()->literal_index()));
- __ Push(instr->hydrogen()->constant_elements());
+ // Boilerplate already exists, constant elements are never accessed.
+ // Pass an empty fixed array.
+ __ Push(Handle<FixedArray>(heap->empty_fixed_array()));
// Pick the right runtime function or stub to call.
int length = instr->hydrogen()->length();
@@ -3861,9 +3877,9 @@
CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
} else {
FastCloneShallowArrayStub::Mode mode =
- constant_elements_kind == FAST_DOUBLE_ELEMENTS
- ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
+ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
FastCloneShallowArrayStub stub(mode, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/array-literal-transitions.js Wed
Oct 19 04:36:55 2011
+++ /branches/bleeding_edge/test/mjsunit/array-literal-transitions.js Fri
Dec 2 04:42:35 2011
@@ -122,4 +122,76 @@
}
%OptimizeFunctionOnNextCall(test_large_literal);
test_large_literal();
-}
+
+ function deopt_array(use_literal) {
+ if (use_literal) {
+ return [.5, 3, 4];
+ } else {
+ return new Array();
+ }
+ }
+
+ deopt_array(false);
+ deopt_array(false);
+ deopt_array(false);
+ %OptimizeFunctionOnNextCall(deopt_array);
+ var array = deopt_array(false);
+ assertTrue(2 != %GetOptimizationStatus(deopt_array));
+ deopt_array(true);
+ assertTrue(1 != %GetOptimizationStatus(deopt_array));
+ array = deopt_array(false);
+ assertTrue(1 != %GetOptimizationStatus(deopt_array));
+
+ // Check that unexpected changes in the objects stored into the
boilerplate
+ // also force a deopt.
+ function deopt_array_literal_all_smis(a) {
+ return [0, 1, a];
+ }
+
+ deopt_array_literal_all_smis(2);
+ deopt_array_literal_all_smis(3);
+ deopt_array_literal_all_smis(4);
+ array = deopt_array_literal_all_smis(4);
+ assertEquals(0, array[0]);
+ assertEquals(1, array[1]);
+ assertEquals(4, array[2]);
+ %OptimizeFunctionOnNextCall(deopt_array_literal_all_smis);
+ array = deopt_array_literal_all_smis(5);
+ array = deopt_array_literal_all_smis(6);
+ assertTrue(2 != %GetOptimizationStatus(deopt_array_literal_all_smis));
+ assertEquals(0, array[0]);
+ assertEquals(1, array[1]);
+ assertEquals(6, array[2]);
+
+ array = deopt_array_literal_all_smis(.5);
+ assertTrue(1 != %GetOptimizationStatus(deopt_array_literal_all_smis));
+ assertEquals(0, array[0]);
+ assertEquals(1, array[1]);
+ assertEquals(.5, array[2]);
+
+ function deopt_array_literal_all_doubles(a) {
+ return [0.5, 1, a];
+ }
+
+ deopt_array_literal_all_doubles(.5);
+ deopt_array_literal_all_doubles(.5);
+ deopt_array_literal_all_doubles(.5);
+ array = deopt_array_literal_all_doubles(0.5);
+ assertEquals(0.5, array[0]);
+ assertEquals(1, array[1]);
+ assertEquals(0.5, array[2]);
+ %OptimizeFunctionOnNextCall(deopt_array_literal_all_doubles);
+ array = deopt_array_literal_all_doubles(5);
+ array = deopt_array_literal_all_doubles(6);
+ assertTrue(2 != %GetOptimizationStatus(deopt_array_literal_all_doubles));
+ assertEquals(0.5, array[0]);
+ assertEquals(1, array[1]);
+ assertEquals(6, array[2]);
+
+ var foo = new Object();
+ array = deopt_array_literal_all_doubles(foo);
+ assertTrue(1 != %GetOptimizationStatus(deopt_array_literal_all_doubles));
+ assertEquals(0.5, array[0]);
+ assertEquals(1, array[1]);
+ assertEquals(foo, array[2]);
+}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev