X87: New hydrogen instruction to reduce cost of growing an array on keyed stores.
authorchunyang.dai <chunyang.dai@intel.com>
Wed, 13 May 2015 11:16:25 +0000 (04:16 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 13 May 2015 11:16:18 +0000 (11:16 +0000)
port 3bce9c3afbe792167a54db49b16679289e0aea8f (r28359).

original commit message:

    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=

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

Cr-Commit-Position: refs/heads/master@{#28388}

src/x87/interface-descriptors-x87.cc
src/x87/lithium-codegen-x87.cc
src/x87/lithium-codegen-x87.h
src/x87/lithium-x87.cc
src/x87/lithium-x87.h

index c9c2775..380b904 100644 (file)
@@ -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) {
index e04ceb2..cee37cf 100644 (file)
@@ -4815,6 +4815,97 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
 }
 
 
+void LCodeGen::DoMaybeGrowElements(LMaybeGrowElements* instr) {
+  class DeferredMaybeGrowElements final : public LDeferredCode {
+   public:
+    DeferredMaybeGrowElements(LCodeGen* codegen,
+                              LMaybeGrowElements* instr,
+                              const X87Stack& x87_stack)
+        : LDeferredCode(codegen, x87_stack), 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, x87_stack_);
+  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());
 
index 5d913c8..8104b1b 100644 (file)
@@ -135,6 +135,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);
index 1b4aa1a..82d746f 100644 (file)
@@ -2397,6 +2397,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() &&
index 7251fda..e1b5c48 100644 (file)
@@ -122,6 +122,7 @@ class LCodeGen;
   V(MathPowHalf)                             \
   V(MathRound)                               \
   V(MathSqrt)                                \
+  V(MaybeGrowElements)                       \
   V(ModByConstI)                             \
   V(ModByPowerOf2I)                          \
   V(ModI)                                    \
@@ -2325,6 +2326,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) {