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
This commit is contained in:
parent
44af185023
commit
7c45d49861
@ -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 };
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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)");
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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) \
|
||||
|
@ -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 };
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
18
test/mjsunit/array-shift2.js
Normal file
18
test/mjsunit/array-shift2.js
Normal file
@ -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]));
|
15
test/mjsunit/array-shift3.js
Normal file
15
test/mjsunit/array-shift3.js
Normal file
@ -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]));
|
Loading…
Reference in New Issue
Block a user