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
This commit is contained in:
parent
3a167b1fbe
commit
da03f275d2
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user