From da03f275d200b1d8c99f9f349013579ee7b21a66 Mon Sep 17 00:00:00 2001 From: "karlklose@chromium.org" Date: Fri, 14 Jan 2011 08:49:52 +0000 Subject: [PATCH] ARM: Implement DoDivI in the lithium code generator. 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 | 13 ++-- src/arm/lithium-codegen-arm.cc | 113 ++++++++++++++++++++++++++++++++- src/arm/lithium-codegen-arm.h | 4 ++ src/arm/macro-assembler-arm.cc | 19 ++++++ src/arm/macro-assembler-arm.h | 13 ++++ src/safepoint-table.cc | 19 ++++++ src/safepoint-table.h | 24 ++++++- 7 files changed, 198 insertions(+), 7 deletions(-) diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index d2d697bc8c..041c034af3 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -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); } diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 3cb4ffeca2..5d4745c437 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -614,6 +614,27 @@ void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, } +void LCodeGen::RecordSafepointWithRegistersAndDoubles( + LPointerMap* pointers, + int arguments, + int deoptimization_index) { + const ZoneList* 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(); } diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index e752e20a5d..4ef772ea93 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -71,6 +71,7 @@ class LCodeGen BASE_EMBEDDED { void FinishCode(Handle 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); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 21146234a1..1028b0e69f 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -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. diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index e997136527..324fbb2dde 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -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)); } diff --git a/src/safepoint-table.cc b/src/safepoint-table.cc index 39b211cd92..e79dcff09a 100644 --- a/src/safepoint-table.cc +++ b/src/safepoint-table.cc @@ -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(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(8)); registers_.Add(new ZoneList(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(8)); + registers_.Add(new ZoneList(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; } diff --git a/src/safepoint-table.h b/src/safepoint-table.h index 6e5e0d8403..d703051423 100644 --- a/src/safepoint-table.h +++ b/src/safepoint-table.h @@ -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 {}; class DeoptimizationIndexField: public BitField {}; // NOLINT + class SaveDoublesField: public BitField { }; // 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);