}
+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 };
}
+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());
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
+ V(ArrayShift) \
V(BitI) \
V(BoundsCheck) \
V(Branch) \
};
+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,
}
+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());
}
+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
}
+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());
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
+ V(ArrayShift) \
V(BitI) \
V(BitS) \
V(BoundsCheck) \
};
+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) {
}
+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());
}
+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,
// List of code stubs used on all platforms.
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
+ V(ArrayShift) \
V(CallFunction) \
V(CallConstruct) \
V(BinaryOpIC) \
};
+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)
case HValue::kArgumentsElements:
case HValue::kArgumentsLength:
case HValue::kArgumentsObject:
+ case HValue::kArrayShift:
case HValue::kBlockEntry:
case HValue::kBoundsCheckBaseIndexInformation:
case HValue::kCallFunction:
}
+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)");
V(ArgumentsElements) \
V(ArgumentsLength) \
V(ArgumentsObject) \
+ V(ArrayShift) \
V(Bitwise) \
V(BlockEntry) \
V(BoundsCheck) \
};
+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,
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;
}
+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 };
}
+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:
}
+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());
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
+ V(ArrayShift) \
V(BitI) \
V(BoundsCheck) \
V(Branch) \
};
+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,
#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) \
}
+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 };
}
+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());
}
+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());
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
+ V(ArrayShift) \
V(BitI) \
V(BoundsCheck) \
V(Branch) \
};
+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,
--- /dev/null
+// 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]));
--- /dev/null
+// 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]));