ARM: Implement DoDivI in the lithium code generator.
authorkarlklose@chromium.org <karlklose@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 14 Jan 2011 08:49:52 +0000 (08:49 +0000)
committerkarlklose@chromium.org <karlklose@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 14 Jan 2011 08:49:52 +0000 (08:49 +0000)
This change provides fast code for a few special cases and calls the GenericBinaryOpStub for the rest.

It also changes the register allocation in the generation of lithium instructions to use fixed registers that are compatible with the generic stub. This allocation can be change once we use a more flexible implementation.

Finally, this change provides infrastructure to save double registers at safepoints, which is need to call the stub in deferred code.

BUG=
TEST=

Review URL: http://codereview.chromium.org/6164005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6304 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/lithium-arm.cc
src/arm/lithium-codegen-arm.cc
src/arm/lithium-codegen-arm.h
src/arm/macro-assembler-arm.cc
src/arm/macro-assembler-arm.h
src/safepoint-table.cc
src/safepoint-table.h

index d2d697bc8c281876014852c004edd81c38cc98e8..041c034af35b550ab13efa86cbe31a8f0432edcc 100644 (file)
@@ -1238,12 +1238,15 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
   if (instr->representation().IsDouble()) {
     return DoArithmeticD(Token::DIV, instr);
   } else if (instr->representation().IsInteger32()) {
-    // The temporary operand is necessary to ensure that right is not allocated
-    // into edx.
-    FixedTemp(r1);
+    // TODO(1042) The fixed register allocation
+    // is needed because we call GenericBinaryOpStub from
+    // the generated code, which requires registers r0
+    // and r1 to be used. We should remove that
+    // when we provide a native implementation.
     LOperand* value = UseFixed(instr->left(), r0);
-    LOperand* divisor = UseRegister(instr->right());
-    return AssignEnvironment(DefineFixed(new LDivI(value, divisor), r0));
+    LOperand* divisor = UseFixed(instr->right(), r1);
+    return AssignEnvironment(AssignPointerMap(
+             DefineFixed(new LDivI(value, divisor), r0)));
   } else {
     return DoArithmeticT(Token::DIV, instr);
   }
index 3cb4ffeca2903a93357d5f53e4ee47ca1b04c5f3..5d4745c437d4ee7fed7cbdc7ded19c8fab9c4b26 100644 (file)
@@ -614,6 +614,27 @@ void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
 }
 
 
+void LCodeGen::RecordSafepointWithRegistersAndDoubles(
+    LPointerMap* pointers,
+    int arguments,
+    int deoptimization_index) {
+  const ZoneList<LOperand*>* operands = pointers->operands();
+  Safepoint safepoint =
+      safepoints_.DefineSafepointWithRegistersAndDoubles(
+          masm(), arguments, deoptimization_index);
+  for (int i = 0; i < operands->length(); i++) {
+    LOperand* pointer = operands->at(i);
+    if (pointer->IsStackSlot()) {
+      safepoint.DefinePointerSlot(pointer->index());
+    } else if (pointer->IsRegister()) {
+      safepoint.DefinePointerRegister(ToRegister(pointer));
+    }
+  }
+  // Register cp always contains a pointer to the context.
+  safepoint.DefinePointerRegister(cp);
+}
+
+
 void LCodeGen::RecordPosition(int position) {
   if (!FLAG_debug_info || position == RelocInfo::kNoPosition) return;
   masm()->positions_recorder()->RecordPosition(position);
@@ -832,7 +853,97 @@ void LCodeGen::DoModI(LModI* instr) {
 
 
 void LCodeGen::DoDivI(LDivI* instr) {
-  Abort("DoDivI unimplemented.");
+  class DeferredDivI: public LDeferredCode {
+   public:
+    DeferredDivI(LCodeGen* codegen, LDivI* instr)
+        : LDeferredCode(codegen), instr_(instr) { }
+    virtual void Generate() { codegen()->DoDeferredDivI(instr_); }
+   private:
+    LDivI* instr_;
+  };
+
+  const Register left = ToRegister(instr->left());
+  const Register right = ToRegister(instr->right());
+  const Register scratch = scratch0();
+  const Register result = ToRegister(instr->result());
+
+  // Check for x / 0.
+  if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+    __ tst(right, right);
+    DeoptimizeIf(eq, instr->environment());
+  }
+
+  // Check for (0 / -x) that will produce negative zero.
+  if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+    Label left_not_zero;
+    __ tst(left, Operand(left));
+    __ b(ne, &left_not_zero);
+    __ tst(right, Operand(right));
+    DeoptimizeIf(mi, instr->environment());
+    __ bind(&left_not_zero);
+  }
+
+  // Check for (-kMinInt / -1).
+  if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+    Label left_not_min_int;
+    __ cmp(left, Operand(kMinInt));
+    __ b(ne, &left_not_min_int);
+    __ cmp(right, Operand(-1));
+    DeoptimizeIf(eq, instr->environment());
+    __ bind(&left_not_min_int);
+  }
+
+  Label done, deoptimize;
+  // Test for a few common cases first.
+  __ cmp(right, Operand(1));
+  __ mov(result, left, LeaveCC, eq);
+  __ b(eq, &done);
+
+  __ cmp(right, Operand(2));
+  __ tst(left, Operand(1), eq);
+  __ mov(result, Operand(left, ASR, 1), LeaveCC, eq);
+  __ b(eq, &done);
+
+  __ cmp(right, Operand(4));
+  __ tst(left, Operand(3), eq);
+  __ mov(result, Operand(left, ASR, 2), LeaveCC, eq);
+  __ b(eq, &done);
+
+  // Call the generic stub. The numbers in r0 and r1 have
+  // to be tagged to Smis. If that is not possible, deoptimize.
+  DeferredDivI* deferred = new DeferredDivI(this, instr);
+
+  __ TrySmiTag(left, &deoptimize, scratch);
+  __ TrySmiTag(right, &deoptimize, scratch);
+
+  __ b(al, deferred->entry());
+  __ bind(deferred->exit());
+
+  // If the result in r0 is a Smi, untag it, else deoptimize.
+  __ BranchOnNotSmi(result, &deoptimize);
+  __ SmiUntag(result);
+  __ b(&done);
+
+  __ bind(&deoptimize);
+  DeoptimizeIf(al, instr->environment());
+  __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredDivI(LDivI* instr) {
+  Register left = ToRegister(instr->left());
+  Register right = ToRegister(instr->right());
+
+  __ PushSafepointRegistersAndDoubles();
+  GenericBinaryOpStub stub(Token::DIV, OVERWRITE_LEFT, left, right);
+  __ CallStub(&stub);
+  RecordSafepointWithRegisters(instr->pointer_map(),
+                               0,
+                               Safepoint::kNoDeoptimizationIndex);
+  // Overwrite the stored value of r0 with the result of the stub.
+  __ str(r0, MemOperand(sp, DwVfpRegister::kNumAllocatableRegisters *
+                        kDoubleSize));
+  __ PopSafepointRegistersAndDoubles();
 }
 
 
index e752e20a5d617bbb04bf72db89567cd731915202..4ef772ea938b4120e35d90f321877183660d1657 100644 (file)
@@ -71,6 +71,7 @@ class LCodeGen BASE_EMBEDDED {
   void FinishCode(Handle<Code> code);
 
   // Deferred code support.
+  void DoDeferredDivI(LDivI* instr);
   void DoDeferredNumberTagD(LNumberTagD* instr);
   void DoDeferredNumberTagI(LNumberTagI* instr);
   void DoDeferredTaggedToI(LTaggedToI* instr);
@@ -200,6 +201,9 @@ class LCodeGen BASE_EMBEDDED {
   void RecordSafepointWithRegisters(LPointerMap* pointers,
                                     int arguments,
                                     int deoptimization_index);
+  void RecordSafepointWithRegistersAndDoubles(LPointerMap* pointers,
+                                              int arguments,
+                                              int deoptimization_index);
   void RecordPosition(int position);
 
   static Condition TokenToCondition(Token::Value op, bool is_unsigned);
index 21146234a1725d790437baf0dc08b4dfa3b359c7..1028b0e69f3e8dbba88fee6e7ac635a0c6ff954b 100644 (file)
@@ -466,6 +466,25 @@ void MacroAssembler::PopSafepointRegisters() {
 }
 
 
+void MacroAssembler::PushSafepointRegistersAndDoubles() {
+  PushSafepointRegisters();
+  sub(sp, sp, Operand(DwVfpRegister::kNumAllocatableRegisters *
+                      kDoubleSize));
+  for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; i++) {
+    vstr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
+  }
+}
+
+
+void MacroAssembler::PopSafepointRegistersAndDoubles() {
+  for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; i++) {
+    vldr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
+  }
+  add(sp, sp, Operand(DwVfpRegister::kNumAllocatableRegisters *
+                      kDoubleSize));
+  PopSafepointRegisters();
+}
+
 int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
   // The registers are pushed starting with the highest encoding,
   // which means that lowest encodings are closest to the stack pointer.
index e997136527985941e3da9aeb5de426f60c2d9e62..324fbb2dde9cdac4675ce28b473a8d1f2a9b0f74 100644 (file)
@@ -232,6 +232,9 @@ class MacroAssembler: public Assembler {
   // RegList constant kSafepointSavedRegisters.
   void PushSafepointRegisters();
   void PopSafepointRegisters();
+  void PushSafepointRegistersAndDoubles();
+  void PopSafepointRegistersAndDoubles();
+
   static int SafepointRegisterStackIndex(int reg_code);
 
   // Load two consecutive registers with two consecutive memory locations.
@@ -716,6 +719,16 @@ class MacroAssembler: public Assembler {
     add(reg, reg, Operand(reg), s);
   }
 
+  // Try to convert int32 to smi. If the value is to large, preserve
+  // the original value and jump to not_a_smi. Destroys scratch and
+  // sets flags.
+  void TrySmiTag(Register reg, Label* not_a_smi, Register scratch) {
+    mov(scratch, reg);
+    SmiTag(scratch, SetCC);
+    b(vs, not_a_smi);
+    mov(reg, scratch);
+  }
+
   void SmiUntag(Register reg) {
     mov(reg, Operand(reg, ASR, kSmiTagSize));
   }
index 39b211cd925e4d52ad66aa7c8d56c9b9f98ad700..e79dcff09a2ccb9504cac4fc3cefc496456d5527 100644 (file)
@@ -125,6 +125,7 @@ Safepoint SafepointTableBuilder::DefineSafepoint(Assembler* assembler,
   pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
   pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
   pc_and_deoptimization_index.arguments = 0;
+  pc_and_deoptimization_index.has_doubles = false;
   deoptimization_info_.Add(pc_and_deoptimization_index);
   indexes_.Add(new ZoneList<int>(8));
   registers_.Add(NULL);
@@ -141,6 +142,7 @@ Safepoint SafepointTableBuilder::DefineSafepointWithRegisters(
   pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
   pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
   pc_and_deoptimization_index.arguments = arguments;
+  pc_and_deoptimization_index.has_doubles = false;
   deoptimization_info_.Add(pc_and_deoptimization_index);
   indexes_.Add(new ZoneList<int>(8));
   registers_.Add(new ZoneList<int>(4));
@@ -148,6 +150,22 @@ Safepoint SafepointTableBuilder::DefineSafepointWithRegisters(
 }
 
 
+Safepoint SafepointTableBuilder::DefineSafepointWithRegistersAndDoubles(
+    Assembler* assembler, int arguments, int deoptimization_index) {
+  ASSERT(deoptimization_index != -1);
+  ASSERT(arguments >= 0);
+  DeoptimizationInfo pc_and_deoptimization_index;
+  pc_and_deoptimization_index.pc = assembler->pc_offset();
+  pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
+  pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
+  pc_and_deoptimization_index.arguments = arguments;
+  pc_and_deoptimization_index.has_doubles = true;
+  deoptimization_info_.Add(pc_and_deoptimization_index);
+  indexes_.Add(new ZoneList<int>(8));
+  registers_.Add(new ZoneList<int>(4));
+  return Safepoint(indexes_.last(), registers_.last());
+}
+
 unsigned SafepointTableBuilder::GetCodeOffset() const {
   ASSERT(emitted_);
   return offset_;
@@ -227,6 +245,7 @@ uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info) {
   uint32_t encoding = SafepointEntry::DeoptimizationIndexField::encode(index);
   encoding |= SafepointEntry::GapCodeSizeField::encode(gap_size);
   encoding |= SafepointEntry::ArgumentsField::encode(info.arguments);
+  encoding |= SafepointEntry::SaveDoublesField::encode(info.has_doubles);
   return encoding;
 }
 
index 6e5e0d84036ae00c6324dbac65a8eb981b9a6078..d703051423ed10de9d4813d34adf9b560666b737 100644 (file)
@@ -73,6 +73,11 @@ class SafepointEntry BASE_EMBEDDED {
     return ArgumentsField::decode(info_);
   }
 
+  bool has_doubles() const {
+    ASSERT(is_valid());
+    return SaveDoublesField::decode(info_);
+  }
+
   uint8_t* bits() {
     ASSERT(is_valid());
     return bits_;
@@ -87,8 +92,9 @@ class SafepointEntry BASE_EMBEDDED {
   // instructions before potentially emitting a constant pool.
   static const int kGapCodeSizeBits = 13;
   static const int kArgumentsFieldBits = 3;
+  static const int kSaveDoublesFieldBits = 1;
   static const int kDeoptIndexBits =
-      32 - kGapCodeSizeBits - kArgumentsFieldBits;
+      32 - kGapCodeSizeBits - kArgumentsFieldBits - kSaveDoublesFieldBits;
   class GapCodeSizeField: public BitField<unsigned, 0, kGapCodeSizeBits> {};
   class DeoptimizationIndexField: public BitField<int,
                                                   kGapCodeSizeBits,
@@ -96,6 +102,11 @@ class SafepointEntry BASE_EMBEDDED {
   class ArgumentsField: public BitField<unsigned,
                                         kGapCodeSizeBits + kDeoptIndexBits,
                                         kArgumentsFieldBits> {};  // NOLINT
+  class SaveDoublesField: public BitField<bool,
+                                          kGapCodeSizeBits + kDeoptIndexBits +
+                                          kArgumentsFieldBits,
+                                          kSaveDoublesFieldBits> { }; // NOLINT
+
  private:
   unsigned info_;
   uint8_t* bits_;
@@ -209,6 +220,16 @@ class SafepointTableBuilder BASE_EMBEDDED {
       int arguments,
       int deoptimization_index = Safepoint::kNoDeoptimizationIndex);
 
+  // Define a new safepoint with all double registers and the normal
+  // registers on the stack for the current position in the body and
+  // take the number of arguments on top of the registers into account.
+  // TODO(1043) Rewrite the three SafepointTableBuilder::DefineSafepoint
+  // methods to one method that uses template arguments.
+  Safepoint DefineSafepointWithRegistersAndDoubles(
+      Assembler* assembler,
+      int arguments,
+      int deoptimization_index = Safepoint::kNoDeoptimizationIndex);
+
   // Update the last safepoint with the size of the code generated for the gap
   // following it.
   void SetPcAfterGap(int pc) {
@@ -227,6 +248,7 @@ class SafepointTableBuilder BASE_EMBEDDED {
     unsigned deoptimization_index;
     unsigned pc_after_gap;
     unsigned arguments;
+    bool has_doubles;
   };
 
   uint32_t EncodeExceptPC(const DeoptimizationInfo& info);