Improve Array.shift() performance for small arrays.
authorbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 9 May 2014 08:28:25 +0000 (08:28 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 9 May 2014 08:28:25 +0000 (08:28 +0000)
TEST=mjsunit/array-shift,mjsunit/array-shift2,mjsunit/array-shift3
R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/279743002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21203 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

24 files changed:
src/arm/code-stubs-arm.cc
src/arm/lithium-arm.cc
src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/arm64/code-stubs-arm64.cc
src/arm64/lithium-arm64.cc
src/arm64/lithium-arm64.h
src/arm64/lithium-codegen-arm64.cc
src/code-stubs-hydrogen.cc
src/code-stubs.h
src/hydrogen-instructions.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/code-stubs-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/objects.h
src/x64/code-stubs-x64.cc
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
src/x64/lithium-x64.h
test/mjsunit/array-shift2.js [new file with mode: 0644]
test/mjsunit/array-shift3.js [new file with mode: 0644]

index 7b29351..2486dd7 100644 (file)
@@ -305,6 +305,16 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
 }
 
 
+void ArrayShiftStub::InitializeInterfaceDescriptor(
+    CodeStubInterfaceDescriptor* descriptor) {
+  static Register registers[] = { r0 };
+  descriptor->register_param_count_ = 1;
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      Builtins::c_function_address(Builtins::c_ArrayShift);
+}
+
+
 void BinaryOpICStub::InitializeInterfaceDescriptor(
     CodeStubInterfaceDescriptor* descriptor) {
   static Register registers[] = { r1, r0 };
index c9201a8..7a4de9f 100644 (file)
@@ -2258,6 +2258,14 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
 }
 
 
+LInstruction* LChunkBuilder::DoArrayShift(HArrayShift* instr) {
+  LOperand* object = UseFixed(instr->object(), r0);
+  LOperand* context = UseFixed(instr->context(), cp);
+  LArrayShift* result = new(zone()) LArrayShift(context, object);
+  return MarkAsCall(DefineFixed(result, r0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
 LInstruction* LChunkBuilder::DoTrapAllocationMemento(
     HTrapAllocationMemento* instr) {
   LOperand* object = UseRegister(instr->object());
index 1a90eb6..b333095 100644 (file)
@@ -26,6 +26,7 @@ class LCodeGen;
   V(ArgumentsLength)                            \
   V(ArithmeticD)                                \
   V(ArithmeticT)                                \
+  V(ArrayShift)                                 \
   V(BitI)                                       \
   V(BoundsCheck)                                \
   V(Branch)                                     \
@@ -2281,6 +2282,21 @@ class LTransitionElementsKind V8_FINAL : public LTemplateInstruction<0, 2, 1> {
 };
 
 
+class LArrayShift V8_FINAL : public LTemplateInstruction<1, 2, 0> {
+ public:
+  LArrayShift(LOperand* context, LOperand* object) {
+    inputs_[0] = context;
+    inputs_[1] = object;
+  }
+
+  LOperand* context() const { return inputs_[0]; }
+  LOperand* object() const { return inputs_[1]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ArrayShift, "array-shift")
+  DECLARE_HYDROGEN_ACCESSOR(ArrayShift)
+};
+
+
 class LTrapAllocationMemento V8_FINAL : public LTemplateInstruction<0, 1, 1> {
  public:
   LTrapAllocationMemento(LOperand* object,
index 5a01d3b..929f57d 100644 (file)
@@ -4435,6 +4435,15 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
 }
 
 
+void LCodeGen::DoArrayShift(LArrayShift* instr) {
+  ASSERT(ToRegister(instr->context()).is(cp));
+  ASSERT(ToRegister(instr->object()).is(r0));
+  ASSERT(ToRegister(instr->result()).is(r0));
+  ArrayShiftStub stub(isolate(), instr->hydrogen()->kind());
+  CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
 void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
   Register object = ToRegister(instr->object());
   Register temp = ToRegister(instr->temp());
index a2dd220..2b08dd0 100644 (file)
@@ -342,6 +342,17 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
 }
 
 
+void ArrayShiftStub::InitializeInterfaceDescriptor(
+    CodeStubInterfaceDescriptor* descriptor) {
+  // x0: receiver
+  static Register registers[] = { x0 };
+  descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]);
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      Builtins::c_function_address(Builtins::c_ArrayShift);
+}
+
+
 void BinaryOpICStub::InitializeInterfaceDescriptor(
     CodeStubInterfaceDescriptor* descriptor) {
   // x1: left operand
index f5f6b8e..6bd7c5b 100644 (file)
@@ -2509,6 +2509,14 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
 }
 
 
+LInstruction* LChunkBuilder::DoArrayShift(HArrayShift* instr) {
+  LOperand* object = UseFixed(instr->object(), x0);
+  LOperand* context = UseFixed(instr->context(), cp);
+  LArrayShift* result = new(zone()) LArrayShift(context, object);
+  return MarkAsCall(DefineFixed(result, x0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
 LInstruction* LChunkBuilder::DoTrapAllocationMemento(
     HTrapAllocationMemento* instr) {
   LOperand* object = UseRegister(instr->object());
index 3abc388..49cd474 100644 (file)
@@ -28,6 +28,7 @@ class LCodeGen;
   V(ArgumentsLength)                            \
   V(ArithmeticD)                                \
   V(ArithmeticT)                                \
+  V(ArrayShift)                                 \
   V(BitI)                                       \
   V(BitS)                                       \
   V(BoundsCheck)                                \
@@ -2852,6 +2853,21 @@ class LTransitionElementsKind V8_FINAL : public LTemplateInstruction<0, 2, 2> {
 };
 
 
+class LArrayShift V8_FINAL : public LTemplateInstruction<1, 2, 0> {
+ public:
+  LArrayShift(LOperand* context, LOperand* object) {
+    inputs_[0] = context;
+    inputs_[1] = object;
+  }
+
+  LOperand* context() const { return inputs_[0]; }
+  LOperand* object() const { return inputs_[1]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ArrayShift, "array-shift")
+  DECLARE_HYDROGEN_ACCESSOR(ArrayShift)
+};
+
+
 class LTrapAllocationMemento V8_FINAL : public LTemplateInstruction<0, 1, 2> {
  public:
   LTrapAllocationMemento(LOperand* object, LOperand* temp1, LOperand* temp2) {
index b064d3d..ef20c67 100644 (file)
@@ -5744,6 +5744,15 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
 }
 
 
+void LCodeGen::DoArrayShift(LArrayShift* instr) {
+  ASSERT(ToRegister(instr->context()).is(cp));
+  ASSERT(ToRegister(instr->object()).is(x0));
+  ASSERT(ToRegister(instr->result()).is(x0));
+  ArrayShiftStub stub(isolate(), instr->hydrogen()->kind());
+  CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
 void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
   Register object = ToRegister(instr->object());
   Register temp1 = ToRegister(instr->temp1());
index 68c8641..a35da69 100644 (file)
@@ -1113,6 +1113,86 @@ Handle<Code> ElementsTransitionAndStoreStub::GenerateCode() {
 }
 
 
+template <>
+HValue* CodeStubGraphBuilder<ArrayShiftStub>::BuildCodeStub() {
+  HValue* receiver = GetParameter(ArrayShiftStub::kReceiver);
+  ElementsKind kind = casted_stub()->kind();
+
+  // We may use double registers for copying.
+  if (IsFastDoubleElementsKind(kind)) info()->MarkAsSavesCallerDoubles();
+
+  HValue* length = Add<HLoadNamedField>(
+      receiver, static_cast<HValue*>(NULL),
+      HObjectAccess::ForArrayLength(kind));
+
+  IfBuilder if_lengthiszero(this);
+  HValue* lengthiszero = if_lengthiszero.If<HCompareNumericAndBranch>(
+      length, graph()->GetConstant0(), Token::EQ);
+  if_lengthiszero.Then();
+  {
+    Push(graph()->GetConstantUndefined());
+  }
+  if_lengthiszero.Else();
+  {
+    // Check if array length is below threshold.
+    IfBuilder if_inline(this);
+    if_inline.If<HCompareNumericAndBranch>(
+        length, Add<HConstant>(ArrayShiftStub::kInlineThreshold), Token::LTE);
+    if_inline.Then();
+    if_inline.ElseDeopt("Array length exceeds threshold");
+    if_inline.End();
+
+    // We cannot handle copy-on-write backing stores here.
+    HValue* elements = AddLoadElements(receiver);
+    if (IsFastSmiOrObjectElementsKind(kind)) {
+      Add<HCheckMaps>(elements, isolate()->factory()->fixed_array_map());
+    }
+
+    // Remember the result.
+    Push(AddElementAccess(elements, graph()->GetConstant0(), NULL,
+                          lengthiszero, kind, LOAD));
+
+    // Compute the new length.
+    HValue* new_length = AddUncasted<HSub>(length, graph()->GetConstant1());
+    new_length->ClearFlag(HValue::kCanOverflow);
+
+    // Copy the remaining elements.
+    LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
+    {
+      HValue* new_key = loop.BeginBody(
+          graph()->GetConstant0(), new_length, Token::LT);
+      HValue* key = AddUncasted<HAdd>(new_key, graph()->GetConstant1());
+      key->ClearFlag(HValue::kCanOverflow);
+      HValue* element = AddUncasted<HLoadKeyed>(
+          elements, key, lengthiszero, kind, ALLOW_RETURN_HOLE);
+      HStoreKeyed* store = Add<HStoreKeyed>(elements, new_key, element, kind);
+      store->SetFlag(HValue::kAllowUndefinedAsNaN);
+    }
+    loop.EndBody();
+
+    // Put a hole at the end.
+    HValue* hole = IsFastSmiOrObjectElementsKind(kind)
+        ? Add<HConstant>(isolate()->factory()->the_hole_value())
+        : Add<HConstant>(FixedDoubleArray::hole_nan_as_double());
+    if (IsFastSmiOrObjectElementsKind(kind)) kind = FAST_HOLEY_ELEMENTS;
+    Add<HStoreKeyed>(elements, new_length, hole, kind, INITIALIZING_STORE);
+
+    // Remember new length.
+    Add<HStoreNamedField>(
+        receiver, HObjectAccess::ForArrayLength(kind),
+        new_length, STORE_TO_INITIALIZED_ENTRY);
+  }
+  if_lengthiszero.End();
+
+  return Pop();
+}
+
+
+Handle<Code> ArrayShiftStub::GenerateCode() {
+  return DoGenerateCode(this);
+}
+
+
 void CodeStubGraphBuilderBase::BuildCheckAndInstallOptimizedCode(
     HValue* js_function,
     HValue* native_context,
index 8380266..b5c07e5 100644 (file)
@@ -16,6 +16,7 @@ namespace internal {
 
 // List of code stubs used on all platforms.
 #define CODE_STUB_LIST_ALL_PLATFORMS(V)  \
+  V(ArrayShift)                          \
   V(CallFunction)                        \
   V(CallConstruct)                       \
   V(BinaryOpIC)                          \
@@ -2466,6 +2467,36 @@ class ElementsTransitionAndStoreStub : public HydrogenCodeStub {
 };
 
 
+class ArrayShiftStub V8_FINAL : public HydrogenCodeStub {
+ public:
+  ArrayShiftStub(Isolate* isolate, ElementsKind kind)
+      : HydrogenCodeStub(isolate), kind_(kind) { }
+
+  ElementsKind kind() const { return kind_; }
+
+  virtual Handle<Code> GenerateCode() V8_OVERRIDE;
+
+  virtual void InitializeInterfaceDescriptor(
+      CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE;
+
+  // Inline Array.shift() for arrays up to this length.
+  static const int kInlineThreshold = 16;
+
+  // Parameters accessed via CodeStubGraphBuilder::GetParameter()
+  static const int kReceiver = 0;
+
+ private:
+  Major MajorKey() { return ArrayShift; }
+  int NotMissMinorKey() {
+    return kind_;
+  }
+
+  ElementsKind kind_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArrayShiftStub);
+};
+
+
 class StoreArrayLiteralElementStub : public PlatformCodeStub {
  public:
   explicit StoreArrayLiteralElementStub(Isolate* isolate)
index f5c5c32..feb8b6b 100644 (file)
@@ -817,6 +817,7 @@ bool HInstruction::CanDeoptimize() {
     case HValue::kArgumentsElements:
     case HValue::kArgumentsLength:
     case HValue::kArgumentsObject:
+    case HValue::kArrayShift:
     case HValue::kBlockEntry:
     case HValue::kBoundsCheckBaseIndexInformation:
     case HValue::kCallFunction:
@@ -3637,6 +3638,12 @@ void HTransitionElementsKind::PrintDataTo(StringStream* stream) {
 }
 
 
+void HArrayShift::PrintDataTo(StringStream* stream) {
+  object()->PrintNameTo(stream);
+  stream->Add(" [%s]", ElementsAccessor::ForKind(kind())->name());
+}
+
+
 void HLoadGlobalCell::PrintDataTo(StringStream* stream) {
   stream->Add("[%p]", *cell().handle());
   if (!details_.IsDontDelete()) stream->Add(" (deleteable)");
index 1cdca4c..85d1e75 100644 (file)
@@ -50,6 +50,7 @@ class LChunkBuilder;
   V(ArgumentsElements)                         \
   V(ArgumentsLength)                           \
   V(ArgumentsObject)                           \
+  V(ArrayShift)                                \
   V(Bitwise)                                   \
   V(BlockEntry)                                \
   V(BoundsCheck)                               \
@@ -7077,6 +7078,51 @@ class HTransitionElementsKind V8_FINAL : public HTemplateInstruction<2> {
 };
 
 
+class HArrayShift V8_FINAL : public HTemplateInstruction<2> {
+ public:
+  static HArrayShift* New(Zone* zone,
+                          HValue* context,
+                          HValue* object,
+                          ElementsKind kind) {
+    return new(zone) HArrayShift(context, object, kind);
+  }
+
+  virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
+    return Representation::Tagged();
+  }
+
+  HValue* context() const { return OperandAt(0); }
+  HValue* object() const { return OperandAt(1); }
+  ElementsKind kind() const { return kind_; }
+
+  virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
+
+  DECLARE_CONCRETE_INSTRUCTION(ArrayShift);
+
+ protected:
+  virtual bool DataEquals(HValue* other) V8_OVERRIDE {
+    HArrayShift* that = HArrayShift::cast(other);
+    return this->kind_ == that->kind_;
+  }
+
+ private:
+  HArrayShift(HValue* context, HValue* object, ElementsKind kind)
+      : kind_(kind) {
+    SetOperandAt(0, context);
+    SetOperandAt(1, object);
+    SetChangesFlag(kNewSpacePromotion);
+    set_representation(Representation::Tagged());
+    if (IsFastSmiOrObjectElementsKind(kind)) {
+      SetChangesFlag(kArrayElements);
+    } else {
+      SetChangesFlag(kDoubleArrayElements);
+    }
+  }
+
+  ElementsKind kind_;
+};
+
+
 class HStringAdd V8_FINAL : public HBinaryOperation {
  public:
   static HInstruction* New(Zone* zone,
index ee9f8e4..d7394ff 100644 (file)
@@ -7835,6 +7835,36 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
       ast_context()->ReturnValue(new_size);
       return true;
     }
+    case kArrayShift: {
+      if (receiver_map.is_null()) return false;
+      if (receiver_map->instance_type() != JS_ARRAY_TYPE) return false;
+      ElementsKind kind = receiver_map->elements_kind();
+      if (!IsFastElementsKind(kind)) return false;
+      if (receiver_map->is_observed()) return false;
+      ASSERT(receiver_map->is_extensible());
+
+      // If there may be elements accessors in the prototype chain, the fast
+      // inlined version can't be used.
+      if (receiver_map->DictionaryElementsInPrototypeChainOnly()) return false;
+
+      // If there currently can be no elements accessors on the prototype chain,
+      // it doesn't mean that there won't be any later. Install a full prototype
+      // chain check to trap element accessors being installed on the prototype
+      // chain, which would cause elements to go to dictionary mode and result
+      // in a map change.
+      BuildCheckPrototypeMaps(
+          handle(JSObject::cast(receiver_map->prototype()), isolate()),
+          Handle<JSObject>::null());
+
+      Drop(expr->arguments()->length());
+      HValue* receiver = Pop();
+      Drop(1);  // function
+
+      receiver = AddCheckMap(receiver, receiver_map);
+      HInstruction* result = NewUncasted<HArrayShift>(receiver, kind);
+      ast_context()->ReturnInstruction(result, expr->id());
+      return true;
+    }
     default:
       // Not yet supported for inlining.
       break;
index 174ebbb..afc0109 100644 (file)
@@ -309,6 +309,16 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
 }
 
 
+void ArrayShiftStub::InitializeInterfaceDescriptor(
+    CodeStubInterfaceDescriptor* descriptor) {
+  static Register registers[] = { eax };
+  descriptor->register_param_count_ = 1;
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      Builtins::c_function_address(Builtins::c_ArrayShift);
+}
+
+
 void BinaryOpICStub::InitializeInterfaceDescriptor(
     CodeStubInterfaceDescriptor* descriptor) {
   static Register registers[] = { edx, eax };
index 2872d4d..38032da 100644 (file)
@@ -4773,6 +4773,15 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
 }
 
 
+void LCodeGen::DoArrayShift(LArrayShift* instr) {
+  ASSERT(ToRegister(instr->context()).is(esi));
+  ASSERT(ToRegister(instr->object()).is(eax));
+  ASSERT(ToRegister(instr->result()).is(eax));
+  ArrayShiftStub stub(isolate(), instr->hydrogen()->kind());
+  CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
 void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
   class DeferredStringCharCodeAt V8_FINAL : public LDeferredCode {
    public:
index 3231095..00fd0e8 100644 (file)
@@ -2355,6 +2355,14 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
 }
 
 
+LInstruction* LChunkBuilder::DoArrayShift(HArrayShift* instr) {
+  LOperand* object = UseFixed(instr->object(), eax);
+  LOperand* context = UseFixed(instr->context(), esi);
+  LArrayShift* result = new(zone()) LArrayShift(context, object);
+  return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
 LInstruction* LChunkBuilder::DoTrapAllocationMemento(
     HTrapAllocationMemento* instr) {
   LOperand* object = UseRegister(instr->object());
index fe6f794..01ffaaf 100644 (file)
@@ -26,6 +26,7 @@ class LCodeGen;
   V(ArgumentsLength)                            \
   V(ArithmeticD)                                \
   V(ArithmeticT)                                \
+  V(ArrayShift)                                 \
   V(BitI)                                       \
   V(BoundsCheck)                                \
   V(Branch)                                     \
@@ -2304,6 +2305,21 @@ class LTransitionElementsKind V8_FINAL : public LTemplateInstruction<0, 2, 2> {
 };
 
 
+class LArrayShift V8_FINAL : public LTemplateInstruction<1, 2, 0> {
+ public:
+  LArrayShift(LOperand* context, LOperand* object) {
+    inputs_[0] = context;
+    inputs_[1] = object;
+  }
+
+  LOperand* context() const { return inputs_[0]; }
+  LOperand* object() const { return inputs_[1]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ArrayShift, "array-shift")
+  DECLARE_HYDROGEN_ACCESSOR(ArrayShift)
+};
+
+
 class LTrapAllocationMemento V8_FINAL  : public LTemplateInstruction<0, 1, 1> {
  public:
   LTrapAllocationMemento(LOperand* object,
index 12f2ee0..f961a28 100644 (file)
@@ -6810,6 +6810,7 @@ class Script: public Struct {
 #define FUNCTIONS_WITH_ID_LIST(V)                   \
   V(Array.prototype, push, ArrayPush)               \
   V(Array.prototype, pop, ArrayPop)                 \
+  V(Array.prototype, shift, ArrayShift)             \
   V(Function.prototype, apply, FunctionApply)       \
   V(String.prototype, charCodeAt, StringCharCodeAt) \
   V(String.prototype, charAt, StringCharAt)         \
index 546595a..6f3fc83 100644 (file)
@@ -305,6 +305,16 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
 }
 
 
+void ArrayShiftStub::InitializeInterfaceDescriptor(
+    CodeStubInterfaceDescriptor* descriptor) {
+  static Register registers[] = { rax };
+  descriptor->register_param_count_ = 1;
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      Builtins::c_function_address(Builtins::c_ArrayShift);
+}
+
+
 void BinaryOpICStub::InitializeInterfaceDescriptor(
     CodeStubInterfaceDescriptor* descriptor) {
   static Register registers[] = { rdx, rax };
index c3dc8ac..0d837ee 100644 (file)
@@ -4400,6 +4400,15 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
 }
 
 
+void LCodeGen::DoArrayShift(LArrayShift* instr) {
+  ASSERT(ToRegister(instr->context()).is(rsi));
+  ASSERT(ToRegister(instr->object()).is(rax));
+  ASSERT(ToRegister(instr->result()).is(rax));
+  ArrayShiftStub stub(isolate(), instr->hydrogen()->kind());
+  CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
 void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
   Register object = ToRegister(instr->object());
   Register temp = ToRegister(instr->temp());
index eb9e7dd..cbade8a 100644 (file)
@@ -2253,6 +2253,14 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
 }
 
 
+LInstruction* LChunkBuilder::DoArrayShift(HArrayShift* instr) {
+  LOperand* object = UseFixed(instr->object(), rax);
+  LOperand* context = UseFixed(instr->context(), rsi);
+  LArrayShift* result = new(zone()) LArrayShift(context, object);
+  return MarkAsCall(DefineFixed(result, rax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
 LInstruction* LChunkBuilder::DoTrapAllocationMemento(
     HTrapAllocationMemento* instr) {
   LOperand* object = UseRegister(instr->object());
index 093b95b..86121f6 100644 (file)
@@ -26,6 +26,7 @@ class LCodeGen;
   V(ArgumentsLength)                            \
   V(ArithmeticD)                                \
   V(ArithmeticT)                                \
+  V(ArrayShift)                                 \
   V(BitI)                                       \
   V(BoundsCheck)                                \
   V(Branch)                                     \
@@ -2245,6 +2246,21 @@ class LTransitionElementsKind V8_FINAL : public LTemplateInstruction<0, 2, 2> {
 };
 
 
+class LArrayShift V8_FINAL : public LTemplateInstruction<1, 2, 0> {
+ public:
+  LArrayShift(LOperand* context, LOperand* object) {
+    inputs_[0] = context;
+    inputs_[1] = object;
+  }
+
+  LOperand* context() const { return inputs_[0]; }
+  LOperand* object() const { return inputs_[1]; }
+
+  DECLARE_CONCRETE_INSTRUCTION(ArrayShift, "array-shift")
+  DECLARE_HYDROGEN_ACCESSOR(ArrayShift)
+};
+
+
 class LTrapAllocationMemento V8_FINAL : public LTemplateInstruction<0, 1, 1> {
  public:
   LTrapAllocationMemento(LOperand* object,
diff --git a/test/mjsunit/array-shift2.js b/test/mjsunit/array-shift2.js
new file mode 100644 (file)
index 0000000..73d8cd4
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+Object.defineProperty(Array.prototype, "1", {
+  get: function() { return "element 1"; },
+  set: function(value) { }
+});
+function test(array) {
+  array.shift();
+  return array;
+}
+assertEquals(["element 1",2], test(["0",,2]));
+assertEquals(["element 1",{}], test([{},,{}]));
+%OptimizeFunctionOnNextCall(test);
+assertEquals(["element 1",0], test([{},,0]));
diff --git a/test/mjsunit/array-shift3.js b/test/mjsunit/array-shift3.js
new file mode 100644 (file)
index 0000000..3a0afc5
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+Array.prototype[1] = "element 1";
+function test(a) {
+  a.shift();
+  return a;
+}
+assertEquals(["element 1",{}], test([0,,{}]));
+assertEquals(["element 1",10], test([9,,10]));
+%OptimizeFunctionOnNextCall(test);
+assertEquals(["element 1",10], test([9,,10]));