From 3bce9c3afbe792167a54db49b16679289e0aea8f Mon Sep 17 00:00:00 2001 From: mvstanton Date: Tue, 12 May 2015 01:47:15 -0700 Subject: [PATCH] New hydrogen instruction to reduce cost of growing an array on keyed stores. HMaybeGrowElements moves the situation where you actually have to grow into deferred code. This means crankshaft doesn't have to spill registers just to make the bounds comparison to see if it'll need to grow or not. It makes the growing case a bit more expensive, but reduces the cost of the general case. BUG=chromium:484025 LOG=N Review URL: https://codereview.chromium.org/1124443004 Cr-Commit-Position: refs/heads/master@{#28359} --- src/arm/interface-descriptors-arm.cc | 1 - src/arm/lithium-arm.cc | 15 +++ src/arm/lithium-arm.h | 23 +++++ src/arm/lithium-codegen-arm.cc | 94 +++++++++++++++++++ src/arm/lithium-codegen-arm.h | 1 + src/arm64/interface-descriptors-arm64.cc | 1 - src/arm64/lithium-arm64.cc | 15 +++ src/arm64/lithium-arm64.h | 23 +++++ src/arm64/lithium-codegen-arm64.cc | 85 +++++++++++++++++ src/arm64/lithium-codegen-arm64.h | 1 + src/code-stubs-hydrogen.cc | 5 +- src/hydrogen-instructions.cc | 1 + src/hydrogen-instructions.h | 59 ++++++++++++ src/hydrogen.cc | 35 ++----- src/ia32/interface-descriptors-ia32.cc | 1 - src/ia32/lithium-codegen-ia32.cc | 89 ++++++++++++++++++ src/ia32/lithium-codegen-ia32.h | 1 + src/ia32/lithium-ia32.cc | 15 +++ src/ia32/lithium-ia32.h | 23 +++++ src/interface-descriptors.cc | 3 +- src/interface-descriptors.h | 3 +- src/mips/interface-descriptors-mips.cc | 1 - src/mips/lithium-codegen-mips.cc | 95 +++++++++++++++++++ src/mips/lithium-codegen-mips.h | 1 + src/mips/lithium-mips.cc | 15 +++ src/mips/lithium-mips.h | 23 +++++ src/mips64/interface-descriptors-mips64.cc | 1 - src/mips64/lithium-codegen-mips64.cc | 95 +++++++++++++++++++ src/mips64/lithium-codegen-mips64.h | 1 + src/mips64/lithium-mips64.cc | 15 +++ src/mips64/lithium-mips64.h | 23 +++++ src/runtime/runtime-array.cc | 2 +- src/runtime/runtime.h | 2 +- src/x64/interface-descriptors-x64.cc | 1 - src/x64/lithium-codegen-x64.cc | 103 +++++++++++++++++++++ src/x64/lithium-codegen-x64.h | 1 + src/x64/lithium-x64.cc | 15 +++ src/x64/lithium-x64.h | 23 +++++ src/x64/macro-assembler-x64.cc | 2 + 39 files changed, 874 insertions(+), 39 deletions(-) diff --git a/src/arm/interface-descriptors-arm.cc b/src/arm/interface-descriptors-arm.cc index 7a2d6595f..7ec37a737 100644 --- a/src/arm/interface-descriptors-arm.cc +++ b/src/arm/interface-descriptors-arm.cc @@ -56,7 +56,6 @@ const Register MathPowIntegerDescriptor::exponent() { const Register GrowArrayElementsDescriptor::ObjectRegister() { return r0; } const Register GrowArrayElementsDescriptor::KeyRegister() { return r3; } -const Register GrowArrayElementsDescriptor::CapacityRegister() { return r2; } void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) { diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index ccd962c23..7dbd9f056 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -2369,6 +2369,21 @@ LInstruction* LChunkBuilder::DoTrapAllocationMemento( } +LInstruction* LChunkBuilder::DoMaybeGrowElements(HMaybeGrowElements* instr) { + info()->MarkAsDeferredCalling(); + LOperand* context = UseFixed(instr->context(), cp); + LOperand* object = Use(instr->object()); + LOperand* elements = Use(instr->elements()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LOperand* current_capacity = UseRegisterOrConstant(instr->current_capacity()); + + LMaybeGrowElements* result = new (zone()) + LMaybeGrowElements(context, object, elements, key, current_capacity); + DefineFixed(result, r0); + return AssignPointerMap(AssignEnvironment(result)); +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool is_in_object = instr->access().IsInobject(); bool needs_write_barrier = instr->NeedsWriteBarrier(); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index e0d4293ee..4c6237211 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -117,6 +117,7 @@ class LCodeGen; V(MathPowHalf) \ V(MathRound) \ V(MathSqrt) \ + V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ V(ModI) \ @@ -2318,6 +2319,28 @@ class LTrapAllocationMemento final : public LTemplateInstruction<0, 1, 1> { }; +class LMaybeGrowElements final : public LTemplateInstruction<1, 5, 0> { + public: + LMaybeGrowElements(LOperand* context, LOperand* object, LOperand* elements, + LOperand* key, LOperand* current_capacity) { + inputs_[0] = context; + inputs_[1] = object; + inputs_[2] = elements; + inputs_[3] = key; + inputs_[4] = current_capacity; + } + + LOperand* context() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + LOperand* elements() { return inputs_[2]; } + LOperand* key() { return inputs_[3]; } + LOperand* current_capacity() { return inputs_[4]; } + + DECLARE_HYDROGEN_ACCESSOR(MaybeGrowElements) + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements, "maybe-grow-elements") +}; + + class LStringAdd final : public LTemplateInstruction<1, 3, 0> { public: LStringAdd(LOperand* context, LOperand* left, LOperand* right) { diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 407bb91d7..0447468a7 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -4506,6 +4506,100 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) { + class DeferredMaybeGrowElements final : public LDeferredCode { + public: + DeferredMaybeGrowElements(LCodeGen* codegen, LMaybeGrowElements* instr) + : LDeferredCode(codegen), instr_(instr) {} + void Generate() override { codegen()->DoDeferredMaybeGrowElements(instr_); } + LInstruction* instr() override { return instr_; } + + private: + LMaybeGrowElements* instr_; + }; + + Register result = r0; + DeferredMaybeGrowElements* deferred = + new (zone()) DeferredMaybeGrowElements(this, instr); + LOperand* key = instr->key(); + LOperand* current_capacity = instr->current_capacity(); + + DCHECK(instr->hydrogen()->key()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->current_capacity()->representation().IsInteger32()); + DCHECK(key->IsConstantOperand() || key->IsRegister()); + DCHECK(current_capacity->IsConstantOperand() || + current_capacity->IsRegister()); + + if (key->IsConstantOperand() && current_capacity->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + if (constant_key >= constant_capacity) { + // Deferred case. + __ jmp(deferred->entry()); + } + } else if (key->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + __ cmp(ToRegister(current_capacity), Operand(constant_key)); + __ b(le, deferred->entry()); + } else if (current_capacity->IsConstantOperand()) { + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + __ cmp(ToRegister(key), Operand(constant_capacity)); + __ b(ge, deferred->entry()); + } else { + __ cmp(ToRegister(key), ToRegister(current_capacity)); + __ b(ge, deferred->entry()); + } + + if (instr->elements()->IsRegister()) { + __ Move(result, ToRegister(instr->elements())); + } else { + __ ldr(result, ToMemOperand(instr->elements())); + } + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredMaybeGrowElements(LMaybeGrowElements* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register result = r0; + __ mov(result, Operand::Zero()); + + // We have to call a stub. + { + PushSafepointRegistersScope scope(this); + if (instr->object()->IsRegister()) { + __ Move(result, ToRegister(instr->object())); + } else { + __ ldr(result, ToMemOperand(instr->object())); + } + + LOperand* key = instr->key(); + if (key->IsConstantOperand()) { + __ Move(r3, Operand(ToSmi(LConstantOperand::cast(key)))); + } else { + __ Move(r3, ToRegister(key)); + __ SmiTag(r3); + } + + GrowArrayElementsStub stub(isolate(), instr->hydrogen()->is_js_array(), + instr->hydrogen()->kind()); + __ CallStub(&stub); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + __ StoreToSafepointRegisterSlot(result, result); + } + + // Deopt on smi, which means the elements array changed to dictionary mode. + __ SmiTst(result); + DeoptimizeIf(eq, instr, Deoptimizer::kSmi); +} + + void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { Register object_reg = ToRegister(instr->object()); Register scratch = scratch0(); diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index 40ab3e83e..add08a1b4 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -112,6 +112,7 @@ class LCodeGen: public LCodeGenBase { void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr); void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); void DoDeferredAllocate(LAllocate* instr); diff --git a/src/arm64/interface-descriptors-arm64.cc b/src/arm64/interface-descriptors-arm64.cc index e1bf9052a..d8f24a01e 100644 --- a/src/arm64/interface-descriptors-arm64.cc +++ b/src/arm64/interface-descriptors-arm64.cc @@ -62,7 +62,6 @@ const Register MathPowIntegerDescriptor::exponent() { return x12; } const Register GrowArrayElementsDescriptor::ObjectRegister() { return x0; } const Register GrowArrayElementsDescriptor::KeyRegister() { return x3; } -const Register GrowArrayElementsDescriptor::CapacityRegister() { return x2; } void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) { diff --git a/src/arm64/lithium-arm64.cc b/src/arm64/lithium-arm64.cc index a4a36bfa1..76b41ec1f 100644 --- a/src/arm64/lithium-arm64.cc +++ b/src/arm64/lithium-arm64.cc @@ -2567,6 +2567,21 @@ LInstruction* LChunkBuilder::DoTrapAllocationMemento( } +LInstruction* LChunkBuilder::DoMaybeGrowElements(HMaybeGrowElements* instr) { + info()->MarkAsDeferredCalling(); + LOperand* context = UseFixed(instr->context(), cp); + LOperand* object = UseRegister(instr->object()); + LOperand* elements = UseRegister(instr->elements()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LOperand* current_capacity = UseRegisterOrConstant(instr->current_capacity()); + + LMaybeGrowElements* result = new (zone()) + LMaybeGrowElements(context, object, elements, key, current_capacity); + DefineFixed(result, x0); + return AssignPointerMap(AssignEnvironment(result)); +} + + LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { LOperand* context = UseFixed(instr->context(), cp); LOperand* value = UseFixed(instr->value(), x3); diff --git a/src/arm64/lithium-arm64.h b/src/arm64/lithium-arm64.h index 7473597b1..c25288ea8 100644 --- a/src/arm64/lithium-arm64.h +++ b/src/arm64/lithium-arm64.h @@ -125,6 +125,7 @@ class LCodeGen; V(MathRoundD) \ V(MathRoundI) \ V(MathSqrt) \ + V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ V(ModI) \ @@ -2637,6 +2638,28 @@ class LStoreNamedGeneric final : public LTemplateInstruction<0, 3, 0> { }; +class LMaybeGrowElements final : public LTemplateInstruction<1, 5, 0> { + public: + LMaybeGrowElements(LOperand* context, LOperand* object, LOperand* elements, + LOperand* key, LOperand* current_capacity) { + inputs_[0] = context; + inputs_[1] = object; + inputs_[2] = elements; + inputs_[3] = key; + inputs_[4] = current_capacity; + } + + LOperand* context() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + LOperand* elements() { return inputs_[2]; } + LOperand* key() { return inputs_[3]; } + LOperand* current_capacity() { return inputs_[4]; } + + DECLARE_HYDROGEN_ACCESSOR(MaybeGrowElements) + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements, "maybe-grow-elements") +}; + + class LStringAdd final : public LTemplateInstruction<1, 3, 0> { public: LStringAdd(LOperand* context, LOperand* left, LOperand* right) { diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index ea259d5f9..b9667ab21 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc @@ -5330,6 +5330,91 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) { + class DeferredMaybeGrowElements final : public LDeferredCode { + public: + DeferredMaybeGrowElements(LCodeGen* codegen, LMaybeGrowElements* instr) + : LDeferredCode(codegen), instr_(instr) {} + void Generate() override { codegen()->DoDeferredMaybeGrowElements(instr_); } + LInstruction* instr() override { return instr_; } + + private: + LMaybeGrowElements* instr_; + }; + + Register result = x0; + DeferredMaybeGrowElements* deferred = + new (zone()) DeferredMaybeGrowElements(this, instr); + LOperand* key = instr->key(); + LOperand* current_capacity = instr->current_capacity(); + + DCHECK(instr->hydrogen()->key()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->current_capacity()->representation().IsInteger32()); + DCHECK(key->IsConstantOperand() || key->IsRegister()); + DCHECK(current_capacity->IsConstantOperand() || + current_capacity->IsRegister()); + + if (key->IsConstantOperand() && current_capacity->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + if (constant_key >= constant_capacity) { + // Deferred case. + __ B(deferred->entry()); + } + } else if (key->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + __ Cmp(ToRegister(current_capacity), Operand(constant_key)); + __ B(le, deferred->entry()); + } else if (current_capacity->IsConstantOperand()) { + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + __ Cmp(ToRegister(key), Operand(constant_capacity)); + __ B(ge, deferred->entry()); + } else { + __ Cmp(ToRegister(key), ToRegister(current_capacity)); + __ B(ge, deferred->entry()); + } + + __ Mov(result, ToRegister(instr->elements())); + + __ Bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredMaybeGrowElements(LMaybeGrowElements* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register result = x0; + __ Mov(result, 0); + + // We have to call a stub. + { + PushSafepointRegistersScope scope(this); + __ Move(result, ToRegister(instr->object())); + + LOperand* key = instr->key(); + if (key->IsConstantOperand()) { + __ Mov(x3, Operand(ToSmi(LConstantOperand::cast(key)))); + } else { + __ Mov(x3, ToRegister(key)); + __ SmiTag(x3); + } + + GrowArrayElementsStub stub(isolate(), instr->hydrogen()->is_js_array(), + instr->hydrogen()->kind()); + __ CallStub(&stub); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + __ StoreToSafepointRegisterSlot(result, result); + } + + // Deopt on smi, which means the elements array changed to dictionary mode. + DeoptimizeIfSmi(result, instr, Deoptimizer::kSmi); +} + + void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { Representation representation = instr->representation(); diff --git a/src/arm64/lithium-codegen-arm64.h b/src/arm64/lithium-codegen-arm64.h index 809ed556d..76ad8d4e2 100644 --- a/src/arm64/lithium-codegen-arm64.h +++ b/src/arm64/lithium-codegen-arm64.h @@ -114,6 +114,7 @@ class LCodeGen: public LCodeGenBase { // Deferred code support. void DoDeferredNumberTagD(LNumberTagD* instr); void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); void DoDeferredMathAbsTagged(LMathAbsTagged* instr, diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index 70f6e6150..c8ea07da2 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -696,10 +696,11 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { HValue* object = GetParameter(GrowArrayElementsDescriptor::kObjectIndex); HValue* key = GetParameter(GrowArrayElementsDescriptor::kKeyIndex); - HValue* current_capacity = - GetParameter(GrowArrayElementsDescriptor::kCapacityIndex); HValue* elements = AddLoadElements(object); + HValue* current_capacity = Add( + elements, nullptr, HObjectAccess::ForFixedArrayLength()); + HValue* length = casted_stub()->is_js_array() ? Add(object, static_cast(NULL), diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 2db3c43c6..974b65b71 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -874,6 +874,7 @@ bool HInstruction::CanDeoptimize() { case HValue::kLoadKeyed: case HValue::kLoadKeyedGeneric: case HValue::kMathFloorOfDiv: + case HValue::kMaybeGrowElements: case HValue::kMod: case HValue::kMul: case HValue::kOsrEntry: diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index fcd7c2c24..ff02212dc 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -127,6 +127,7 @@ class LChunkBuilder; V(MapEnumLength) \ V(MathFloorOfDiv) \ V(MathMinMax) \ + V(MaybeGrowElements) \ V(Mod) \ V(Mul) \ V(OsrEntry) \ @@ -965,6 +966,12 @@ std::ostream& operator<<(std::ostream& os, const ChangesOf& v); return new (zone) I(context, p1, p2, p3, p4, p5); \ } +#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P6(I, P1, P2, P3, P4, P5, P6) \ + static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ + P3 p3, P4 p4, P5 p5, P6 p6) { \ + return new (zone) I(context, p1, p2, p3, p4, p5, p6); \ + } + // A helper class to represent per-operand position information attached to // the HInstruction in the compact form. Uses tagging to distinguish between @@ -7558,6 +7565,58 @@ class HTrapAllocationMemento final : public HTemplateInstruction<1> { }; +class HMaybeGrowElements final : public HTemplateInstruction<5> { + public: + DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P6(HMaybeGrowElements, HValue*, + HValue*, HValue*, HValue*, bool, + ElementsKind); + + Representation RequiredInputRepresentation(int index) override { + if (index < 3) { + return Representation::Tagged(); + } + DCHECK(index == 3 || index == 4); + return Representation::Integer32(); + } + + HValue* context() const { return OperandAt(0); } + HValue* object() const { return OperandAt(1); } + HValue* elements() const { return OperandAt(2); } + HValue* key() const { return OperandAt(3); } + HValue* current_capacity() const { return OperandAt(4); } + + bool is_js_array() const { return is_js_array_; } + ElementsKind kind() const { return kind_; } + + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements) + + protected: + bool DataEquals(HValue* other) override { return true; } + + private: + explicit HMaybeGrowElements(HValue* context, HValue* object, HValue* elements, + HValue* key, HValue* current_capacity, + bool is_js_array, ElementsKind kind) { + is_js_array_ = is_js_array; + kind_ = kind; + + SetOperandAt(0, context); + SetOperandAt(1, object); + SetOperandAt(2, elements); + SetOperandAt(3, key); + SetOperandAt(4, current_capacity); + + SetFlag(kUseGVN); + SetChangesFlag(kElementsPointer); + SetChangesFlag(kNewSpacePromotion); + set_representation(Representation::Tagged()); + } + + bool is_js_array_; + ElementsKind kind_; +}; + + class HToFastProperties final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HToFastProperties, HValue*); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index ead064e3c..af25700de 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1331,38 +1331,23 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow( HValue* current_capacity = AddLoadFixedArrayLength(elements); - IfBuilder capacity_checker(this); - - capacity_checker.If(key, current_capacity, - Token::GTE); - capacity_checker.Then(); - - // BuildCheckAndGrowElementsCapacity could de-opt without profitable feedback - // therefore we defer calling it to a stub in optimized functions. It is - // okay to call directly in a code stub though, because a bailout to the - // runtime is tolerable in the corner cases. if (top_info()->IsStub()) { + IfBuilder capacity_checker(this); + capacity_checker.If(key, current_capacity, + Token::GTE); + capacity_checker.Then(); HValue* new_elements = BuildCheckAndGrowElementsCapacity( object, elements, kind, length, current_capacity, key); environment()->Push(new_elements); + capacity_checker.Else(); + environment()->Push(elements); + capacity_checker.End(); } else { - GrowArrayElementsStub stub(isolate(), is_js_array, kind); - GrowArrayElementsDescriptor descriptor(isolate()); - HConstant* target = Add(stub.GetCode()); - HValue* op_vals[] = {context(), object, key, current_capacity}; - HValue* new_elements = Add( - target, 0, descriptor, Vector(op_vals, 4)); - // If the object changed to a dictionary, GrowArrayElements will return a - // smi to signal that deopt is required. - Add(new_elements); - environment()->Push(new_elements); + HValue* result = Add( + object, elements, key, current_capacity, is_js_array, kind); + environment()->Push(result); } - capacity_checker.Else(); - - environment()->Push(elements); - capacity_checker.End(); - if (is_js_array) { HValue* new_length = AddUncasted(key, graph_->GetConstant1()); new_length->ClearFlag(HValue::kCanOverflow); diff --git a/src/ia32/interface-descriptors-ia32.cc b/src/ia32/interface-descriptors-ia32.cc index bb4cb2bb4..b7c71ca28 100644 --- a/src/ia32/interface-descriptors-ia32.cc +++ b/src/ia32/interface-descriptors-ia32.cc @@ -58,7 +58,6 @@ const Register MathPowIntegerDescriptor::exponent() { const Register GrowArrayElementsDescriptor::ObjectRegister() { return eax; } const Register GrowArrayElementsDescriptor::KeyRegister() { return ebx; } -const Register GrowArrayElementsDescriptor::CapacityRegister() { return ecx; } void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) { diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index addce601a..cb92f2d1b 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -4363,6 +4363,95 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) { } +void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) { + class DeferredMaybeGrowElements final : public LDeferredCode { + public: + DeferredMaybeGrowElements(LCodeGen* codegen, LMaybeGrowElements* instr) + : LDeferredCode(codegen), instr_(instr) {} + void Generate() override { codegen()->DoDeferredMaybeGrowElements(instr_); } + LInstruction* instr() override { return instr_; } + + private: + LMaybeGrowElements* instr_; + }; + + Register result = eax; + DeferredMaybeGrowElements* deferred = + new (zone()) DeferredMaybeGrowElements(this, instr); + LOperand* key = instr->key(); + LOperand* current_capacity = instr->current_capacity(); + + DCHECK(instr->hydrogen()->key()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->current_capacity()->representation().IsInteger32()); + DCHECK(key->IsConstantOperand() || key->IsRegister()); + DCHECK(current_capacity->IsConstantOperand() || + current_capacity->IsRegister()); + + if (key->IsConstantOperand() && current_capacity->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + if (constant_key >= constant_capacity) { + // Deferred case. + __ jmp(deferred->entry()); + } + } else if (key->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + __ cmp(ToOperand(current_capacity), Immediate(constant_key)); + __ j(less_equal, deferred->entry()); + } else if (current_capacity->IsConstantOperand()) { + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + __ cmp(ToRegister(key), Immediate(constant_capacity)); + __ j(greater_equal, deferred->entry()); + } else { + __ cmp(ToRegister(key), ToRegister(current_capacity)); + __ j(greater_equal, deferred->entry()); + } + + __ mov(result, ToOperand(instr->elements())); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredMaybeGrowElements(LMaybeGrowElements* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register result = eax; + __ Move(result, Immediate(0)); + + // We have to call a stub. + { + PushSafepointRegistersScope scope(this); + if (instr->object()->IsRegister()) { + __ Move(result, ToRegister(instr->object())); + } else { + __ mov(result, ToOperand(instr->object())); + } + + LOperand* key = instr->key(); + if (key->IsConstantOperand()) { + __ mov(ebx, ToImmediate(key, Representation::Smi())); + } else { + __ Move(ebx, ToRegister(key)); + __ SmiTag(ebx); + } + + GrowArrayElementsStub stub(isolate(), instr->hydrogen()->is_js_array(), + instr->hydrogen()->kind()); + __ CallStub(&stub); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + __ StoreToSafepointRegisterSlot(result, result); + } + + // Deopt on smi, which means the elements array changed to dictionary mode. + __ test(result, Immediate(kSmiTagMask)); + DeoptimizeIf(equal, instr, Deoptimizer::kSmi); +} + + void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { Register object_reg = ToRegister(instr->object()); diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index 32ea6bf17..256fa81d4 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -101,6 +101,7 @@ class LCodeGen: public LCodeGenBase { void DoDeferredTaggedToI(LTaggedToI* instr, Label* done); void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr); void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); void DoDeferredAllocate(LAllocate* instr); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index b085555eb..1c0f80af0 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -2393,6 +2393,21 @@ LInstruction* LChunkBuilder::DoTrapAllocationMemento( } +LInstruction* LChunkBuilder::DoMaybeGrowElements(HMaybeGrowElements* instr) { + info()->MarkAsDeferredCalling(); + LOperand* context = UseFixed(instr->context(), esi); + LOperand* object = Use(instr->object()); + LOperand* elements = Use(instr->elements()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LOperand* current_capacity = UseRegisterOrConstant(instr->current_capacity()); + + LMaybeGrowElements* result = new (zone()) + LMaybeGrowElements(context, object, elements, key, current_capacity); + DefineFixed(result, eax); + return AssignPointerMap(AssignEnvironment(result)); +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool is_in_object = instr->access().IsInobject(); bool is_external_location = instr->access().IsExternalMemory() && diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index 4d245ecb5..c40c371a5 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -121,6 +121,7 @@ class LCodeGen; V(MathPowHalf) \ V(MathRound) \ V(MathSqrt) \ + V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ V(ModI) \ @@ -2319,6 +2320,28 @@ class LTrapAllocationMemento final : public LTemplateInstruction<0, 1, 1> { }; +class LMaybeGrowElements final : public LTemplateInstruction<1, 5, 0> { + public: + LMaybeGrowElements(LOperand* context, LOperand* object, LOperand* elements, + LOperand* key, LOperand* current_capacity) { + inputs_[0] = context; + inputs_[1] = object; + inputs_[2] = elements; + inputs_[3] = key; + inputs_[4] = current_capacity; + } + + LOperand* context() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + LOperand* elements() { return inputs_[2]; } + LOperand* key() { return inputs_[3]; } + LOperand* current_capacity() { return inputs_[4]; } + + DECLARE_HYDROGEN_ACCESSOR(MaybeGrowElements) + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements, "maybe-grow-elements") +}; + + class LStringAdd final : public LTemplateInstruction<1, 3, 0> { public: LStringAdd(LOperand* context, LOperand* left, LOperand* right) { diff --git a/src/interface-descriptors.cc b/src/interface-descriptors.cc index 4c7a1159b..a9b234bfb 100644 --- a/src/interface-descriptors.cc +++ b/src/interface-descriptors.cc @@ -149,8 +149,7 @@ void ContextOnlyDescriptor::Initialize(CallInterfaceDescriptorData* data) { void GrowArrayElementsDescriptor::Initialize( CallInterfaceDescriptorData* data) { - Register registers[] = {ContextRegister(), ObjectRegister(), KeyRegister(), - CapacityRegister()}; + Register registers[] = {ContextRegister(), ObjectRegister(), KeyRegister()}; data->Initialize(arraysize(registers), registers, NULL); } } diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h index 6f69f3c8c..63e814a0c 100644 --- a/src/interface-descriptors.h +++ b/src/interface-descriptors.h @@ -545,10 +545,9 @@ class GrowArrayElementsDescriptor : public CallInterfaceDescriptor { public: DECLARE_DESCRIPTOR(GrowArrayElementsDescriptor, CallInterfaceDescriptor) - enum RegisterInfo { kObjectIndex, kKeyIndex, kCapacityIndex }; + enum RegisterInfo { kObjectIndex, kKeyIndex }; static const Register ObjectRegister(); static const Register KeyRegister(); - static const Register CapacityRegister(); }; #undef DECLARE_DESCRIPTOR diff --git a/src/mips/interface-descriptors-mips.cc b/src/mips/interface-descriptors-mips.cc index e02b9759c..6647ca705 100644 --- a/src/mips/interface-descriptors-mips.cc +++ b/src/mips/interface-descriptors-mips.cc @@ -56,7 +56,6 @@ const Register MathPowIntegerDescriptor::exponent() { const Register GrowArrayElementsDescriptor::ObjectRegister() { return a0; } const Register GrowArrayElementsDescriptor::KeyRegister() { return a3; } -const Register GrowArrayElementsDescriptor::CapacityRegister() { return a2; } void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) { diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index 9c6631653..4cc01d863 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -4486,6 +4486,101 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) { + class DeferredMaybeGrowElements final : public LDeferredCode { + public: + DeferredMaybeGrowElements(LCodeGen* codegen, LMaybeGrowElements* instr) + : LDeferredCode(codegen), instr_(instr) {} + void Generate() override { codegen()->DoDeferredMaybeGrowElements(instr_); } + LInstruction* instr() override { return instr_; } + + private: + LMaybeGrowElements* instr_; + }; + + Register result = v0; + DeferredMaybeGrowElements* deferred = + new (zone()) DeferredMaybeGrowElements(this, instr); + LOperand* key = instr->key(); + LOperand* current_capacity = instr->current_capacity(); + + DCHECK(instr->hydrogen()->key()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->current_capacity()->representation().IsInteger32()); + DCHECK(key->IsConstantOperand() || key->IsRegister()); + DCHECK(current_capacity->IsConstantOperand() || + current_capacity->IsRegister()); + + if (key->IsConstantOperand() && current_capacity->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + if (constant_key >= constant_capacity) { + // Deferred case. + __ jmp(deferred->entry()); + } + } else if (key->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + __ Branch(deferred->entry(), le, ToRegister(current_capacity), + Operand(constant_key)); + } else if (current_capacity->IsConstantOperand()) { + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + __ Branch(deferred->entry(), ge, ToRegister(key), + Operand(constant_capacity)); + } else { + __ Branch(deferred->entry(), ge, ToRegister(key), + Operand(ToRegister(current_capacity))); + } + + if (instr->elements()->IsRegister()) { + __ mov(result, ToRegister(instr->elements())); + } else { + __ lw(result, ToMemOperand(instr->elements())); + } + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredMaybeGrowElements(LMaybeGrowElements* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register result = v0; + __ mov(result, zero_reg); + + // We have to call a stub. + { + PushSafepointRegistersScope scope(this); + if (instr->object()->IsRegister()) { + __ mov(result, ToRegister(instr->object())); + } else { + __ lw(result, ToMemOperand(instr->object())); + } + + LOperand* key = instr->key(); + if (key->IsConstantOperand()) { + __ li(a3, Operand(ToSmi(LConstantOperand::cast(key)))); + } else { + __ mov(a3, ToRegister(key)); + __ SmiTag(a3); + } + + GrowArrayElementsStub stub(isolate(), instr->hydrogen()->is_js_array(), + instr->hydrogen()->kind()); + __ mov(a0, result); + __ CallStub(&stub); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + __ StoreToSafepointRegisterSlot(result, result); + } + + // Deopt on smi, which means the elements array changed to dictionary mode. + __ SmiTst(result, at); + DeoptimizeIf(eq, instr, Deoptimizer::kSmi, at, Operand(zero_reg)); +} + + void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { Register object_reg = ToRegister(instr->object()); Register scratch = scratch0(); diff --git a/src/mips/lithium-codegen-mips.h b/src/mips/lithium-codegen-mips.h index 6d75c4c1f..a68359656 100644 --- a/src/mips/lithium-codegen-mips.h +++ b/src/mips/lithium-codegen-mips.h @@ -110,6 +110,7 @@ class LCodeGen: public LCodeGenBase { void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr); void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); void DoDeferredAllocate(LAllocate* instr); diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index 06b8d51f5..cbd59aa5f 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -2316,6 +2316,21 @@ LInstruction* LChunkBuilder::DoTrapAllocationMemento( } +LInstruction* LChunkBuilder::DoMaybeGrowElements(HMaybeGrowElements* instr) { + info()->MarkAsDeferredCalling(); + LOperand* context = UseFixed(instr->context(), cp); + LOperand* object = Use(instr->object()); + LOperand* elements = Use(instr->elements()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LOperand* current_capacity = UseRegisterOrConstant(instr->current_capacity()); + + LMaybeGrowElements* result = new (zone()) + LMaybeGrowElements(context, object, elements, key, current_capacity); + DefineFixed(result, v0); + return AssignPointerMap(AssignEnvironment(result)); +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool is_in_object = instr->access().IsInobject(); bool needs_write_barrier = instr->NeedsWriteBarrier(); diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h index 667487014..dcef2f626 100644 --- a/src/mips/lithium-mips.h +++ b/src/mips/lithium-mips.h @@ -117,6 +117,7 @@ class LCodeGen; V(MathPowHalf) \ V(MathRound) \ V(MathSqrt) \ + V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ V(ModI) \ @@ -2275,6 +2276,28 @@ class LTrapAllocationMemento final : public LTemplateInstruction<0, 1, 1> { }; +class LMaybeGrowElements final : public LTemplateInstruction<1, 5, 0> { + public: + LMaybeGrowElements(LOperand* context, LOperand* object, LOperand* elements, + LOperand* key, LOperand* current_capacity) { + inputs_[0] = context; + inputs_[1] = object; + inputs_[2] = elements; + inputs_[3] = key; + inputs_[4] = current_capacity; + } + + LOperand* context() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + LOperand* elements() { return inputs_[2]; } + LOperand* key() { return inputs_[3]; } + LOperand* current_capacity() { return inputs_[4]; } + + DECLARE_HYDROGEN_ACCESSOR(MaybeGrowElements) + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements, "maybe-grow-elements") +}; + + class LStringAdd final : public LTemplateInstruction<1, 3, 0> { public: LStringAdd(LOperand* context, LOperand* left, LOperand* right) { diff --git a/src/mips64/interface-descriptors-mips64.cc b/src/mips64/interface-descriptors-mips64.cc index 6a4329f8b..2219edade 100644 --- a/src/mips64/interface-descriptors-mips64.cc +++ b/src/mips64/interface-descriptors-mips64.cc @@ -56,7 +56,6 @@ const Register MathPowIntegerDescriptor::exponent() { const Register GrowArrayElementsDescriptor::ObjectRegister() { return a0; } const Register GrowArrayElementsDescriptor::KeyRegister() { return a3; } -const Register GrowArrayElementsDescriptor::CapacityRegister() { return a2; } void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) { diff --git a/src/mips64/lithium-codegen-mips64.cc b/src/mips64/lithium-codegen-mips64.cc index e8f7234d2..65275d081 100644 --- a/src/mips64/lithium-codegen-mips64.cc +++ b/src/mips64/lithium-codegen-mips64.cc @@ -4580,6 +4580,101 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) { + class DeferredMaybeGrowElements final : public LDeferredCode { + public: + DeferredMaybeGrowElements(LCodeGen* codegen, LMaybeGrowElements* instr) + : LDeferredCode(codegen), instr_(instr) {} + void Generate() override { codegen()->DoDeferredMaybeGrowElements(instr_); } + LInstruction* instr() override { return instr_; } + + private: + LMaybeGrowElements* instr_; + }; + + Register result = v0; + DeferredMaybeGrowElements* deferred = + new (zone()) DeferredMaybeGrowElements(this, instr); + LOperand* key = instr->key(); + LOperand* current_capacity = instr->current_capacity(); + + DCHECK(instr->hydrogen()->key()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->current_capacity()->representation().IsInteger32()); + DCHECK(key->IsConstantOperand() || key->IsRegister()); + DCHECK(current_capacity->IsConstantOperand() || + current_capacity->IsRegister()); + + if (key->IsConstantOperand() && current_capacity->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + if (constant_key >= constant_capacity) { + // Deferred case. + __ jmp(deferred->entry()); + } + } else if (key->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + __ Branch(deferred->entry(), le, ToRegister(current_capacity), + Operand(constant_key)); + } else if (current_capacity->IsConstantOperand()) { + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + __ Branch(deferred->entry(), ge, ToRegister(key), + Operand(constant_capacity)); + } else { + __ Branch(deferred->entry(), ge, ToRegister(key), + Operand(ToRegister(current_capacity))); + } + + if (instr->elements()->IsRegister()) { + __ mov(result, ToRegister(instr->elements())); + } else { + __ ld(result, ToMemOperand(instr->elements())); + } + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredMaybeGrowElements(LMaybeGrowElements* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register result = v0; + __ mov(result, zero_reg); + + // We have to call a stub. + { + PushSafepointRegistersScope scope(this); + if (instr->object()->IsRegister()) { + __ mov(result, ToRegister(instr->object())); + } else { + __ ld(result, ToMemOperand(instr->object())); + } + + LOperand* key = instr->key(); + if (key->IsConstantOperand()) { + __ li(a3, Operand(ToSmi(LConstantOperand::cast(key)))); + } else { + __ mov(a3, ToRegister(key)); + __ SmiTag(a3); + } + + GrowArrayElementsStub stub(isolate(), instr->hydrogen()->is_js_array(), + instr->hydrogen()->kind()); + __ mov(a0, result); + __ CallStub(&stub); + RecordSafepointWithLazyDeopt( + instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); + __ StoreToSafepointRegisterSlot(result, result); + } + + // Deopt on smi, which means the elements array changed to dictionary mode. + __ SmiTst(result, at); + DeoptimizeIf(eq, instr, Deoptimizer::kSmi, at, Operand(zero_reg)); +} + + void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { Register object_reg = ToRegister(instr->object()); Register scratch = scratch0(); diff --git a/src/mips64/lithium-codegen-mips64.h b/src/mips64/lithium-codegen-mips64.h index 0db3677d5..a9539a7e6 100644 --- a/src/mips64/lithium-codegen-mips64.h +++ b/src/mips64/lithium-codegen-mips64.h @@ -111,6 +111,7 @@ class LCodeGen: public LCodeGenBase { void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr); void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); void DoDeferredAllocate(LAllocate* instr); diff --git a/src/mips64/lithium-mips64.cc b/src/mips64/lithium-mips64.cc index 1f518d347..e723c0490 100644 --- a/src/mips64/lithium-mips64.cc +++ b/src/mips64/lithium-mips64.cc @@ -2316,6 +2316,21 @@ LInstruction* LChunkBuilder::DoTrapAllocationMemento( } +LInstruction* LChunkBuilder::DoMaybeGrowElements(HMaybeGrowElements* instr) { + info()->MarkAsDeferredCalling(); + LOperand* context = UseFixed(instr->context(), cp); + LOperand* object = Use(instr->object()); + LOperand* elements = Use(instr->elements()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LOperand* current_capacity = UseRegisterOrConstant(instr->current_capacity()); + + LMaybeGrowElements* result = new (zone()) + LMaybeGrowElements(context, object, elements, key, current_capacity); + DefineFixed(result, v0); + return AssignPointerMap(AssignEnvironment(result)); +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool is_in_object = instr->access().IsInobject(); bool needs_write_barrier = instr->NeedsWriteBarrier(); diff --git a/src/mips64/lithium-mips64.h b/src/mips64/lithium-mips64.h index adc2a4faa..bae2740b5 100644 --- a/src/mips64/lithium-mips64.h +++ b/src/mips64/lithium-mips64.h @@ -117,6 +117,7 @@ class LCodeGen; V(MathPowHalf) \ V(MathRound) \ V(MathSqrt) \ + V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ V(ModI) \ @@ -2257,6 +2258,28 @@ class LTrapAllocationMemento final : public LTemplateInstruction<0, 1, 1> { }; +class LMaybeGrowElements final : public LTemplateInstruction<1, 5, 0> { + public: + LMaybeGrowElements(LOperand* context, LOperand* object, LOperand* elements, + LOperand* key, LOperand* current_capacity) { + inputs_[0] = context; + inputs_[1] = object; + inputs_[2] = elements; + inputs_[3] = key; + inputs_[4] = current_capacity; + } + + LOperand* context() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + LOperand* elements() { return inputs_[2]; } + LOperand* key() { return inputs_[3]; } + LOperand* current_capacity() { return inputs_[4]; } + + DECLARE_HYDROGEN_ACCESSOR(MaybeGrowElements) + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements, "maybe-grow-elements") +}; + + class LStringAdd final : public LTemplateInstruction<1, 3, 0> { public: LStringAdd(LOperand* context, LOperand* left, LOperand* right) { diff --git a/src/runtime/runtime-array.cc b/src/runtime/runtime-array.cc index 49cbc5fd0..d40557474 100644 --- a/src/runtime/runtime-array.cc +++ b/src/runtime/runtime-array.cc @@ -1241,7 +1241,7 @@ RUNTIME_FUNCTION(Runtime_NormalizeElements) { // GrowArrayElements returns a sentinel Smi if the object was normalized. RUNTIME_FUNCTION(Runtime_GrowArrayElements) { HandleScope scope(isolate); - DCHECK(args.length() == 3); + DCHECK(args.length() == 2); CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); CONVERT_NUMBER_CHECKED(int, key, Int32, args[1]); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index a2b6b5010..ff1d759cf 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -43,7 +43,7 @@ namespace internal { F(ArrayConstructorWithSubclassing, -1, 1) \ F(InternalArrayConstructor, -1, 1) \ F(NormalizeElements, 1, 1) \ - F(GrowArrayElements, 3, 1) \ + F(GrowArrayElements, 2, 1) \ F(HasComplexElements, 1, 1) \ F(ForInCacheArrayLength, 2, 1) /* TODO(turbofan): Only temporary */ \ F(IsArray, 1, 1) \ diff --git a/src/x64/interface-descriptors-x64.cc b/src/x64/interface-descriptors-x64.cc index 83aa5714c..46c049181 100644 --- a/src/x64/interface-descriptors-x64.cc +++ b/src/x64/interface-descriptors-x64.cc @@ -58,7 +58,6 @@ const Register MathPowIntegerDescriptor::exponent() { const Register GrowArrayElementsDescriptor::ObjectRegister() { return rax; } const Register GrowArrayElementsDescriptor::KeyRegister() { return rbx; } -const Register GrowArrayElementsDescriptor::CapacityRegister() { return rcx; } void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) { diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 80b3267e7..3fe4121da 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -4549,6 +4549,109 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) { + class DeferredMaybeGrowElements final : public LDeferredCode { + public: + DeferredMaybeGrowElements(LCodeGen* codegen, LMaybeGrowElements* instr) + : LDeferredCode(codegen), instr_(instr) {} + void Generate() override { codegen()->DoDeferredMaybeGrowElements(instr_); } + LInstruction* instr() override { return instr_; } + + private: + LMaybeGrowElements* instr_; + }; + + Register result = rax; + DeferredMaybeGrowElements* deferred = + new (zone()) DeferredMaybeGrowElements(this, instr); + LOperand* key = instr->key(); + LOperand* current_capacity = instr->current_capacity(); + + DCHECK(instr->hydrogen()->key()->representation().IsInteger32()); + DCHECK(instr->hydrogen()->current_capacity()->representation().IsInteger32()); + DCHECK(key->IsConstantOperand() || key->IsRegister()); + DCHECK(current_capacity->IsConstantOperand() || + current_capacity->IsRegister()); + + if (key->IsConstantOperand() && current_capacity->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + if (constant_key >= constant_capacity) { + // Deferred case. + __ jmp(deferred->entry()); + } + } else if (key->IsConstantOperand()) { + int32_t constant_key = ToInteger32(LConstantOperand::cast(key)); + __ cmpl(ToRegister(current_capacity), Immediate(constant_key)); + __ j(less_equal, deferred->entry()); + } else if (current_capacity->IsConstantOperand()) { + int32_t constant_capacity = + ToInteger32(LConstantOperand::cast(current_capacity)); + __ cmpl(ToRegister(key), Immediate(constant_capacity)); + __ j(greater_equal, deferred->entry()); + } else { + __ cmpl(ToRegister(key), ToRegister(current_capacity)); + __ j(greater_equal, deferred->entry()); + } + + if (instr->elements()->IsRegister()) { + __ movp(result, ToRegister(instr->elements())); + } else { + __ movp(result, ToOperand(instr->elements())); + } + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredMaybeGrowElements(LMaybeGrowElements* instr) { + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + Register result = rax; + __ Move(result, Smi::FromInt(0)); + + // We have to call a stub. + { + PushSafepointRegistersScope scope(this); + if (instr->object()->IsConstantOperand()) { + LConstantOperand* constant_object = + LConstantOperand::cast(instr->object()); + if (IsSmiConstant(constant_object)) { + Smi* immediate = ToSmi(constant_object); + __ Move(result, immediate); + } else { + Handle handle_value = ToHandle(constant_object); + __ Move(result, handle_value); + } + } else if (instr->object()->IsRegister()) { + __ Move(result, ToRegister(instr->object())); + } else { + __ movp(result, ToOperand(instr->object())); + } + + LOperand* key = instr->key(); + if (key->IsConstantOperand()) { + __ Move(rbx, ToSmi(LConstantOperand::cast(key))); + } else { + __ Move(rbx, ToRegister(key)); + __ Integer32ToSmi(rbx, rbx); + } + + GrowArrayElementsStub stub(isolate(), instr->hydrogen()->is_js_array(), + instr->hydrogen()->kind()); + __ CallStub(&stub); + RecordSafepointWithLazyDeopt(instr, RECORD_SAFEPOINT_WITH_REGISTERS, 0); + __ StoreToSafepointRegisterSlot(result, result); + } + + // Deopt on smi, which means the elements array changed to dictionary mode. + Condition is_smi = __ CheckSmi(result); + DeoptimizeIf(is_smi, instr, Deoptimizer::kSmi); +} + + void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { Register object_reg = ToRegister(instr->object()); diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index 5fb3173b0..4a0ed7d9e 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -95,6 +95,7 @@ class LCodeGen: public LCodeGenBase { void DoDeferredTaggedToI(LTaggedToI* instr, Label* done); void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr); void DoDeferredStackCheck(LStackCheck* instr); + void DoDeferredMaybeGrowElements(LMaybeGrowElements* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); void DoDeferredAllocate(LAllocate* instr); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 54aed57b8..5ee844669 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -2375,6 +2375,21 @@ LInstruction* LChunkBuilder::DoTrapAllocationMemento( } +LInstruction* LChunkBuilder::DoMaybeGrowElements(HMaybeGrowElements* instr) { + info()->MarkAsDeferredCalling(); + LOperand* context = UseFixed(instr->context(), rsi); + LOperand* object = Use(instr->object()); + LOperand* elements = Use(instr->elements()); + LOperand* key = UseRegisterOrConstant(instr->key()); + LOperand* current_capacity = UseRegisterOrConstant(instr->current_capacity()); + + LMaybeGrowElements* result = new (zone()) + LMaybeGrowElements(context, object, elements, key, current_capacity); + DefineFixed(result, rax); + return AssignPointerMap(AssignEnvironment(result)); +} + + LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool is_in_object = instr->access().IsInobject(); bool is_external_location = instr->access().IsExternalMemory() && diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index b7f2ab9b0..381155fd1 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -117,6 +117,7 @@ class LCodeGen; V(MathPowHalf) \ V(MathRound) \ V(MathSqrt) \ + V(MaybeGrowElements) \ V(ModByConstI) \ V(ModByPowerOf2I) \ V(ModI) \ @@ -2294,6 +2295,28 @@ class LTrapAllocationMemento final : public LTemplateInstruction<0, 1, 1> { }; +class LMaybeGrowElements final : public LTemplateInstruction<1, 5, 0> { + public: + LMaybeGrowElements(LOperand* context, LOperand* object, LOperand* elements, + LOperand* key, LOperand* current_capacity) { + inputs_[0] = context; + inputs_[1] = object; + inputs_[2] = elements; + inputs_[3] = key; + inputs_[4] = current_capacity; + } + + LOperand* context() { return inputs_[0]; } + LOperand* object() { return inputs_[1]; } + LOperand* elements() { return inputs_[2]; } + LOperand* key() { return inputs_[3]; } + LOperand* current_capacity() { return inputs_[4]; } + + DECLARE_HYDROGEN_ACCESSOR(MaybeGrowElements) + DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements, "maybe-grow-elements") +}; + + class LStringAdd final : public LTemplateInstruction<1, 3, 0> { public: LStringAdd(LOperand* context, LOperand* left, LOperand* right) { diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 0e70826df..9adf6a45a 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -925,6 +925,8 @@ Register MacroAssembler::GetSmiConstant(Smi* source) { void MacroAssembler::LoadSmiConstant(Register dst, Smi* source) { + // Special-casing 0 here to use xorl seems to make things slower, so we don't + // do it. Move(dst, source, Assembler::RelocInfoNone()); } -- 2.34.1