From 7c45d49861a593e9be2259c33c29c6f79db4d57c Mon Sep 17 00:00:00 2001 From: "bmeurer@chromium.org" Date: Fri, 9 May 2014 08:28:25 +0000 Subject: [PATCH] Improve Array.shift() performance for small arrays. 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 --- src/arm/code-stubs-arm.cc | 10 ++++ src/arm/lithium-arm.cc | 8 +++ src/arm/lithium-arm.h | 16 ++++++ src/arm/lithium-codegen-arm.cc | 9 ++++ src/arm64/code-stubs-arm64.cc | 11 ++++ src/arm64/lithium-arm64.cc | 8 +++ src/arm64/lithium-arm64.h | 16 ++++++ src/arm64/lithium-codegen-arm64.cc | 9 ++++ src/code-stubs-hydrogen.cc | 80 ++++++++++++++++++++++++++++++ src/code-stubs.h | 31 ++++++++++++ src/hydrogen-instructions.cc | 7 +++ src/hydrogen-instructions.h | 46 +++++++++++++++++ src/hydrogen.cc | 30 +++++++++++ src/ia32/code-stubs-ia32.cc | 10 ++++ src/ia32/lithium-codegen-ia32.cc | 9 ++++ src/ia32/lithium-ia32.cc | 8 +++ src/ia32/lithium-ia32.h | 16 ++++++ src/objects.h | 1 + src/x64/code-stubs-x64.cc | 10 ++++ src/x64/lithium-codegen-x64.cc | 9 ++++ src/x64/lithium-x64.cc | 8 +++ src/x64/lithium-x64.h | 16 ++++++ test/mjsunit/array-shift2.js | 18 +++++++ test/mjsunit/array-shift3.js | 15 ++++++ 24 files changed, 401 insertions(+) create mode 100644 test/mjsunit/array-shift2.js create mode 100644 test/mjsunit/array-shift3.js diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 7b2935106..2486dd702 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -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 }; diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index c9201a807..7a4de9f4c 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -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()); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index 1a90eb638..b333095c7 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -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, diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 5a01d3bc8..929f57d99 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -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()); diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index a2dd22058..2b08dd0c7 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc @@ -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 diff --git a/src/arm64/lithium-arm64.cc b/src/arm64/lithium-arm64.cc index f5f6b8e2c..6bd7c5b58 100644 --- a/src/arm64/lithium-arm64.cc +++ b/src/arm64/lithium-arm64.cc @@ -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()); diff --git a/src/arm64/lithium-arm64.h b/src/arm64/lithium-arm64.h index 3abc388fe..49cd4743a 100644 --- a/src/arm64/lithium-arm64.h +++ b/src/arm64/lithium-arm64.h @@ -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) { diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index b064d3da9..ef20c67f8 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc @@ -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()); diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index 68c864176..a35da69f5 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -1113,6 +1113,86 @@ Handle ElementsTransitionAndStoreStub::GenerateCode() { } +template <> +HValue* CodeStubGraphBuilder::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( + receiver, static_cast(NULL), + HObjectAccess::ForArrayLength(kind)); + + IfBuilder if_lengthiszero(this); + HValue* lengthiszero = if_lengthiszero.If( + 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( + length, Add(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(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(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(new_key, graph()->GetConstant1()); + key->ClearFlag(HValue::kCanOverflow); + HValue* element = AddUncasted( + elements, key, lengthiszero, kind, ALLOW_RETURN_HOLE); + HStoreKeyed* store = Add(elements, new_key, element, kind); + store->SetFlag(HValue::kAllowUndefinedAsNaN); + } + loop.EndBody(); + + // Put a hole at the end. + HValue* hole = IsFastSmiOrObjectElementsKind(kind) + ? Add(isolate()->factory()->the_hole_value()) + : Add(FixedDoubleArray::hole_nan_as_double()); + if (IsFastSmiOrObjectElementsKind(kind)) kind = FAST_HOLEY_ELEMENTS; + Add(elements, new_length, hole, kind, INITIALIZING_STORE); + + // Remember new length. + Add( + receiver, HObjectAccess::ForArrayLength(kind), + new_length, STORE_TO_INITIALIZED_ENTRY); + } + if_lengthiszero.End(); + + return Pop(); +} + + +Handle ArrayShiftStub::GenerateCode() { + return DoGenerateCode(this); +} + + void CodeStubGraphBuilderBase::BuildCheckAndInstallOptimizedCode( HValue* js_function, HValue* native_context, diff --git a/src/code-stubs.h b/src/code-stubs.h index 8380266d9..b5c07e5a0 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -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 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) diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index f5c5c32f4..feb8b6b48 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -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)"); diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 1cdca4c46..85d1e7549 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -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, diff --git a/src/hydrogen.cc b/src/hydrogen.cc index ee9f8e416..d7394ff3b 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -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::null()); + + Drop(expr->arguments()->length()); + HValue* receiver = Pop(); + Drop(1); // function + + receiver = AddCheckMap(receiver, receiver_map); + HInstruction* result = NewUncasted(receiver, kind); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } default: // Not yet supported for inlining. break; diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 174ebbbfa..afc010959 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -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 }; diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 2872d4dc0..38032dacd 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -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: diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 3231095ad..00fd0e8c0 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -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()); diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index fe6f79463..01ffaaf69 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -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, diff --git a/src/objects.h b/src/objects.h index 12f2ee0e5..f961a28d7 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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) \ diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 546595ad4..6f3fc8378 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -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 }; diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index c3dc8ac30..0d837eecd 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -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()); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index eb9e7dd00..cbade8a7d 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -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()); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 093b95b4d..86121f62a 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -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 index 000000000..73d8cd4ff --- /dev/null +++ b/test/mjsunit/array-shift2.js @@ -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 index 000000000..3a0afc596 --- /dev/null +++ b/test/mjsunit/array-shift3.js @@ -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])); -- 2.34.1