// Update the write barrier for the map field.
__ RecordWriteField(receiver_reg, HeapObject::kMapOffset, scratch1, scratch2,
- OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+ kDontSaveFPRegs, OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
if (details.type() == CONSTANT) {
DCHECK(value_reg.is(eax));
__ mov(storage_reg, value_reg);
}
__ RecordWriteField(receiver_reg, offset, storage_reg, scratch1,
- EMIT_REMEMBERED_SET, smi_check);
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, smi_check);
}
} else {
// Write to the properties array.
__ mov(storage_reg, value_reg);
}
__ RecordWriteField(scratch1, offset, storage_reg, receiver_reg,
- EMIT_REMEMBERED_SET, smi_check);
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, smi_check);
}
}
// Update write barrier. Make sure not to clobber the value.
__ mov(r1, value);
- __ RecordWrite(elements, r0, r1);
+ __ RecordWrite(elements, r0, r1, kDontSaveFPRegs);
}
__ mov(mapped_location, value);
__ lea(ecx, mapped_location);
__ mov(edx, value);
- __ RecordWrite(ebx, ecx, edx);
+ __ RecordWrite(ebx, ecx, edx, kDontSaveFPRegs);
__ Ret();
__ bind(¬in);
// The unmapped lookup expects that the parameter map is in ebx.
__ mov(unmapped_location, value);
__ lea(edi, unmapped_location);
__ mov(edx, value);
- __ RecordWrite(ebx, edi, edx);
+ __ RecordWrite(ebx, edi, edx, kDontSaveFPRegs);
__ Ret();
__ bind(&slow);
GenerateMiss(masm);
__ mov(FixedArrayElementOperand(ebx, key), value);
// Update write barrier for the elements array address.
__ mov(edx, value); // Preserve the value which is returned.
- __ RecordWriteArray(ebx, edx, key, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+ __ RecordWriteArray(ebx, edx, key, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
__ ret(0);
__ bind(fast_double);
namespace v8 {
namespace internal {
-bool CpuFeatures::SupportsCrankshaft() { return false; }
+bool CpuFeatures::SupportsCrankshaft() { return true; }
static const byte kCallOpcode = 0xE8;
}
+void Assembler::fldcw(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ emit_operand(ebp, adr);
+}
+
+
+void Assembler::fnstcw(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ emit_operand(edi, adr);
+}
+
+
void Assembler::fstp_d(const Operand& adr) {
EnsureSpace ensure_space(this);
EMIT(0xDD);
}
+void Assembler::fsqrt() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xFA);
+}
+
+
void Assembler::fcos() {
EnsureSpace ensure_space(this);
EMIT(0xD9);
}
+void Assembler::fadd_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDC);
+ emit_operand(eax, adr);
+}
+
+
void Assembler::fsub(int i) {
EnsureSpace ensure_space(this);
emit_farith(0xDC, 0xE8, i);
}
+void Assembler::fxam() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xE5);
+}
+
+
void Assembler::fucomp(int i) {
EnsureSpace ensure_space(this);
emit_farith(0xDD, 0xE8, i);
}
+void Assembler::fnsave(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDD);
+ emit_operand(esi, adr);
+}
+
+
+void Assembler::frstor(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDD);
+ emit_operand(esp, adr);
+}
+
+
void Assembler::sahf() {
EnsureSpace ensure_space(this);
EMIT(0x9E);
struct X87Register {
- static const int kMaxNumAllocatableRegisters = 8;
+ static const int kMaxNumAllocatableRegisters = 6;
static const int kMaxNumRegisters = 8;
static int NumAllocatableRegisters() {
return kMaxNumAllocatableRegisters;
void fabs();
void fchs();
+ void fsqrt();
void fcos();
void fsin();
void fptan();
void fadd(int i);
void fadd_i(int i);
+ void fadd_d(const Operand& adr);
void fsub(int i);
void fsub_i(int i);
void fmul(int i);
void ffree(int i = 0);
void ftst();
+ void fxam();
void fucomp(int i);
void fucompp();
void fucomi(int i);
void fucomip();
void fcompp();
void fnstsw_ax();
+ void fldcw(const Operand& adr);
+ void fnstcw(const Operand& adr);
void fwait();
void fnclex();
+ void fnsave(const Operand& adr);
+ void frstor(const Operand& adr);
void frndint();
}
-static void Generate_NotifyStubFailureHelper(MacroAssembler* masm) {
+static void Generate_NotifyStubFailureHelper(MacroAssembler* masm,
+ SaveFPRegsMode save_doubles) {
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
// stubs that tail call the runtime on deopts passing their parameters in
// registers.
__ pushad();
- __ CallRuntime(Runtime::kNotifyStubFailure, 0);
+ __ CallRuntime(Runtime::kNotifyStubFailure, 0, save_doubles);
__ popad();
// Tear down internal frame.
}
void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) {
- Generate_NotifyStubFailureHelper(masm);
+ Generate_NotifyStubFailureHelper(masm, kDontSaveFPRegs);
}
void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) {
- // SaveDoubles is meanless for X87, just used by deoptimizer.cc
- Generate_NotifyStubFailureHelper(masm);
+ Generate_NotifyStubFailureHelper(masm, kSaveFPRegs);
}
// store the registers in any particular way, but we do have to store and
// restore them.
__ pushad();
+ if (save_doubles()) {
+ // Save FPU stat in m108byte.
+ __ sub(esp, Immediate(108));
+ __ fnsave(Operand(esp, 0));
+ }
const int argument_count = 1;
AllowExternalCallThatCantCauseGC scope(masm);
__ CallCFunction(
ExternalReference::store_buffer_overflow_function(isolate()),
argument_count);
+ if (save_doubles()) {
+ // Restore FPU stat in m108byte.
+ __ frstor(Operand(esp, 0));
+ __ add(esp, Immediate(108));
+ }
__ popad();
__ ret(0);
}
__ mov(eax, Operand(esp, kSubjectOffset));
__ mov(ecx, eax);
__ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
- __ RecordWriteField(ebx,
- RegExpImpl::kLastSubjectOffset,
- eax,
- edi);
+ __ RecordWriteField(ebx, RegExpImpl::kLastSubjectOffset, eax, edi,
+ kDontSaveFPRegs);
__ mov(eax, ecx);
__ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
- __ RecordWriteField(ebx,
- RegExpImpl::kLastInputOffset,
- eax,
- edi);
+ __ RecordWriteField(ebx, RegExpImpl::kLastInputOffset, eax, edi,
+ kDontSaveFPRegs);
// Get the static offsets vector filled by the native regexp code.
ExternalReference address_of_static_offsets_vector =
__ push(edi);
__ push(ebx);
__ push(edx);
- __ RecordWriteArray(ebx, edi, edx, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+ __ RecordWriteArray(ebx, edi, edx, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
__ pop(edx);
__ pop(ebx);
__ pop(edi);
void CodeStub::GenerateFPStubs(Isolate* isolate) {
- // Do nothing.
+ CEntryStub save_doubles(isolate, 1, kSaveFPRegs);
+ // Stubs might already be in the snapshot, detect that and don't regenerate,
+ // which would lead to code stub initialization state being messed up.
+ Code* save_doubles_code;
+ if (!save_doubles.FindCodeInCache(&save_doubles_code)) {
+ save_doubles_code = *(save_doubles.GetCode());
+ }
+ isolate->set_fp_stubs_generated(true);
}
void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
- CEntryStub stub(isolate, 1);
+ CEntryStub stub(isolate, 1, kDontSaveFPRegs);
stub.GetCode();
}
ProfileEntryHookStub::MaybeCallEntryHook(masm);
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame();
+ __ EnterExitFrame(save_doubles());
// ebx: pointer to C function (C callee-saved)
// ebp: frame pointer (restored after C call)
}
// Exit the JavaScript to C++ exit frame.
- __ LeaveExitFrame();
+ __ LeaveExitFrame(save_doubles());
__ ret(0);
// Handling of exception.
Isolate* isolate) {
StoreBufferOverflowStub stub(isolate, kDontSaveFPRegs);
stub.GetCode();
+ StoreBufferOverflowStub stub2(isolate, kSaveFPRegs);
+ stub2.GetCode();
}
__ jmp(&skip_to_incremental_compacting, Label::kFar);
if (remembered_set_action() == EMIT_REMEMBERED_SET) {
- __ RememberedSetHelper(object(), address(), value(),
+ __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(),
MacroAssembler::kReturnAtEnd);
} else {
__ ret(0);
mode);
InformIncrementalMarker(masm);
regs_.Restore(masm);
- __ RememberedSetHelper(object(), address(), value(),
+ __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(),
MacroAssembler::kReturnAtEnd);
__ bind(&dont_need_remembered_set);
void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm) {
- regs_.SaveCallerSaveRegisters(masm);
+ regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode());
int argument_count = 3;
__ PrepareCallCFunction(argument_count, regs_.scratch0());
__ mov(Operand(esp, 0 * kPointerSize), regs_.object());
ExternalReference::incremental_marking_record_write_function(isolate()),
argument_count);
- regs_.RestoreCallerSaveRegisters(masm);
+ regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode());
}
regs_.Restore(masm);
if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
- __ RememberedSetHelper(object(), address(), value(),
+ __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(),
MacroAssembler::kReturnAtEnd);
} else {
__ ret(0);
regs_.Restore(masm);
if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
- __ RememberedSetHelper(object(), address(), value(),
+ __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(),
MacroAssembler::kReturnAtEnd);
} else {
__ ret(0);
FixedArrayBase::kHeaderSize));
__ mov(Operand(ecx, 0), eax);
// Update the write barrier for the array store.
- __ RecordWrite(ebx, ecx, eax,
- EMIT_REMEMBERED_SET,
+ __ RecordWrite(ebx, ecx, eax, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
__ ret(0);
void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
- CEntryStub ces(isolate(), 1);
+ CEntryStub ces(isolate(), 1, kSaveFPRegs);
__ call(ces.GetCode(), RelocInfo::CODE_TARGET);
int parameter_count_offset =
StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
class RecordWriteStub: public PlatformCodeStub {
public:
- RecordWriteStub(Isolate* isolate,
- Register object,
- Register value,
- Register address,
- RememberedSetAction remembered_set_action)
+ RecordWriteStub(Isolate* isolate, Register object, Register value,
+ Register address, RememberedSetAction remembered_set_action,
+ SaveFPRegsMode fp_mode)
: PlatformCodeStub(isolate),
regs_(object, // An input reg.
address, // An input reg.
minor_key_ = ObjectBits::encode(object.code()) |
ValueBits::encode(value.code()) |
AddressBits::encode(address.code()) |
- RememberedSetActionBits::encode(remembered_set_action);
+ RememberedSetActionBits::encode(remembered_set_action) |
+ SaveFPRegsModeBits::encode(fp_mode);
}
RecordWriteStub(uint32_t key, Isolate* isolate)
// saved registers that were not already preserved. The caller saved
// registers are eax, ecx and edx. The three scratch registers (incl. ecx)
// will be restored by other means so we don't bother pushing them here.
- void SaveCallerSaveRegisters(MacroAssembler* masm) {
+ void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->push(eax);
if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->push(edx);
+ if (mode == kSaveFPRegs) {
+ // Save FPU state in m108byte.
+ masm->sub(esp, Immediate(108));
+ masm->fnsave(Operand(esp, 0));
+ }
}
- inline void RestoreCallerSaveRegisters(MacroAssembler*masm) {
+ inline void RestoreCallerSaveRegisters(MacroAssembler* masm,
+ SaveFPRegsMode mode) {
+ if (mode == kSaveFPRegs) {
+ // Restore FPU state in m108byte.
+ masm->frstor(Operand(esp, 0));
+ masm->add(esp, Immediate(108));
+ }
if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->pop(edx);
if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->pop(eax);
}
return RememberedSetActionBits::decode(minor_key_);
}
+ SaveFPRegsMode save_fp_regs_mode() const {
+ return SaveFPRegsModeBits::decode(minor_key_);
+ }
+
class ObjectBits: public BitField<int, 0, 3> {};
class ValueBits: public BitField<int, 3, 3> {};
class AddressBits: public BitField<int, 6, 3> {};
class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {};
+ class SaveFPRegsModeBits : public BitField<SaveFPRegsMode, 10, 1> {};
RegisterAllocation regs_;
// Set transitioned map.
__ mov(FieldOperand(receiver, HeapObject::kMapOffset), target_map);
- __ RecordWriteField(receiver,
- HeapObject::kMapOffset,
- target_map,
- scratch,
- EMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, scratch,
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
}
// Replace receiver's backing store with newly created FixedDoubleArray.
__ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
__ mov(ebx, eax);
- __ RecordWriteField(edx,
- JSObject::kElementsOffset,
- ebx,
- edi,
- EMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteField(edx, JSObject::kElementsOffset, ebx, edi, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
__ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset));
// ebx: target map
// Set transitioned map.
__ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
- __ RecordWriteField(edx,
- HeapObject::kMapOffset,
- ebx,
- edi,
- OMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteField(edx, HeapObject::kMapOffset, ebx, edi, kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
}
// Set transitioned map.
__ bind(&only_change_map);
__ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
- __ RecordWriteField(edx,
- HeapObject::kMapOffset,
- ebx,
- edi,
- OMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteField(edx, HeapObject::kMapOffset, ebx, edi, kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
__ jmp(&success);
// Call into runtime if GC is required.
__ mov(FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize), esi);
__ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx);
__ mov(esi, ebx);
- __ RecordWriteArray(eax,
- edx,
- esi,
- EMIT_REMEMBERED_SET,
+ __ RecordWriteArray(eax, edx, esi, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
__ jmp(&entry, Label::kNear);
// edx: receiver
// Set transitioned map.
__ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
- __ RecordWriteField(edx,
- HeapObject::kMapOffset,
- ebx,
- edi,
- OMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteField(edx, HeapObject::kMapOffset, ebx, edi, kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
// Replace receiver's backing store with newly created and filled FixedArray.
__ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
- __ RecordWriteField(edx,
- JSObject::kElementsOffset,
- eax,
- edi,
- EMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteField(edx, JSObject::kElementsOffset, eax, edi, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
// Restore registers.
__ pop(eax);
void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
- // Do nothing for X87.
- return;
+ for (int i = 0; i < X87Register::kMaxNumAllocatableRegisters; ++i) {
+ double double_value = input_->GetDoubleRegister(i);
+ output_frame->SetDoubleRegister(i, double_value);
+ }
}
// Save all general purpose registers before messing with them.
const int kNumberOfRegisters = Register::kNumRegisters;
+
+ const int kDoubleRegsSize =
+ kDoubleSize * X87Register::kMaxNumAllocatableRegisters;
+
+ // Reserve space for x87 fp registers.
+ __ sub(esp, Immediate(kDoubleRegsSize));
+
__ pushad();
- const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize;
+ // GP registers are safe to use now.
+ // Save used x87 fp registers in correct position of previous reserve space.
+ Label loop, done;
+ // Get the layout of x87 stack.
+ __ sub(esp, Immediate(kPointerSize));
+ __ fistp_s(MemOperand(esp, 0));
+ __ pop(eax);
+ // Preserve stack layout in edi
+ __ mov(edi, eax);
+ // Get the x87 stack depth, the first 3 bits.
+ __ mov(ecx, eax);
+ __ and_(ecx, 0x7);
+ __ j(zero, &done, Label::kNear);
+
+ __ bind(&loop);
+ __ shr(eax, 0x3);
+ __ mov(ebx, eax);
+ __ and_(ebx, 0x7); // Extract the st_x index into ebx.
+ // Pop TOS to the correct position. The disp(0x20) is due to pushad.
+ // The st_i should be saved to (esp + ebx * kDoubleSize + 0x20).
+ __ fstp_d(Operand(esp, ebx, times_8, 0x20));
+ __ dec(ecx); // Decrease stack depth.
+ __ j(not_zero, &loop, Label::kNear);
+ __ bind(&done);
+
+ const int kSavedRegistersAreaSize =
+ kNumberOfRegisters * kPointerSize + kDoubleRegsSize;
// Get the bailout id from the stack.
__ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
__ sub(edx, ebp);
__ neg(edx);
+ __ push(edi);
// Allocate a new deoptimizer object.
__ PrepareCallCFunction(6, eax);
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
}
+ __ pop(edi);
+
// Preserve deoptimizer object in register eax and get the input
// frame descriptor pointer.
__ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
__ pop(Operand(ebx, offset));
}
+ int double_regs_offset = FrameDescription::double_registers_offset();
+ // Fill in the double input registers.
+ for (int i = 0; i < X87Register::kMaxNumAllocatableRegisters; ++i) {
+ int dst_offset = i * kDoubleSize + double_regs_offset;
+ int src_offset = i * kDoubleSize;
+ __ fld_d(Operand(esp, src_offset));
+ __ fstp_d(Operand(ebx, dst_offset));
+ }
+
// Clear FPU all exceptions.
// TODO(ulan): Find out why the TOP register is not zero here in some cases,
// and check that the generated code never deoptimizes with unbalanced stack.
__ fnclex();
// Remove the bailout id, return address and the double registers.
- __ add(esp, Immediate(2 * kPointerSize));
+ __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
// Compute a pointer to the unwinding limit in register ecx; that is
// the first stack slot not part of the input frame.
__ j(not_equal, &pop_loop);
// Compute the output frame in the deoptimizer.
+ __ push(edi);
__ push(eax);
__ PrepareCallCFunction(1, ebx);
__ mov(Operand(esp, 0 * kPointerSize), eax);
ExternalReference::compute_output_frames_function(isolate()), 1);
}
__ pop(eax);
+ __ pop(edi);
// If frame was dynamically aligned, pop padding.
Label no_padding;
__ cmp(eax, edx);
__ j(below, &outer_push_loop);
+
+ // In case of a failed STUB, we have to restore the x87 stack.
+ // x87 stack layout is in edi.
+ Label loop2, done2;
+ // Get the x87 stack depth, the first 3 bits.
+ __ mov(ecx, edi);
+ __ and_(ecx, 0x7);
+ __ j(zero, &done2, Label::kNear);
+
+ __ lea(ecx, Operand(ecx, ecx, times_2, 0));
+ __ bind(&loop2);
+ __ mov(eax, edi);
+ __ shr_cl(eax);
+ __ and_(eax, 0x7);
+ __ fld_d(Operand(ebx, eax, times_8, double_regs_offset));
+ __ sub(ecx, Immediate(0x3));
+ __ j(not_zero, &loop2, Label::kNear);
+ __ bind(&done2);
+
// Push state, pc, and continuation from the last output frame.
__ push(Operand(ebx, FrameDescription::state_offset()));
__ push(Operand(ebx, FrameDescription::pc_offset()));
case 0: mnem = "fld_s"; break;
case 2: mnem = "fst_s"; break;
case 3: mnem = "fstp_s"; break;
- case 7: mnem = "fstcw"; break;
+ case 5:
+ mnem = "fldcw";
+ break;
+ case 7:
+ mnem = "fnstcw";
+ break;
default: UnimplementedInstruction();
}
break;
}
break;
+ case 0xDC:
+ switch (regop) {
+ case 0:
+ mnem = "fadd_d";
+ break;
+ default:
+ UnimplementedInstruction();
+ }
+ break;
+
case 0xDD: switch (regop) {
case 0: mnem = "fld_d"; break;
case 1: mnem = "fisttp_d"; break;
case 2: mnem = "fst_d"; break;
case 3: mnem = "fstp_d"; break;
+ case 4:
+ mnem = "frstor";
+ break;
+ case 6:
+ mnem = "fnsave";
+ break;
default: UnimplementedInstruction();
}
break;
__ mov(Operand(esi, context_offset), eax);
// Update the write barrier. This clobbers eax and ebx.
if (need_write_barrier) {
- __ RecordWriteContextSlot(esi,
- context_offset,
- eax,
- ebx);
+ __ RecordWriteContextSlot(esi, context_offset, eax, ebx,
+ kDontSaveFPRegs);
} else if (FLAG_debug_code) {
Label done;
__ JumpIfInNewSpace(esi, eax, &done, Label::kNear);
if (var->IsContextSlot()) {
int offset = Context::SlotOffset(var->index());
DCHECK(!scratch0.is(esi) && !src.is(esi) && !scratch1.is(esi));
- __ RecordWriteContextSlot(scratch0, offset, src, scratch1);
+ __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs);
}
}
VisitForAccumulatorValue(declaration->fun());
__ mov(ContextOperand(esi, variable->index()), result_register());
// We know that we have written a function, which is not a smi.
- __ RecordWriteContextSlot(esi,
- Context::SlotOffset(variable->index()),
- result_register(),
- ecx,
- EMIT_REMEMBERED_SET,
- OMIT_SMI_CHECK);
+ __ RecordWriteContextSlot(esi, Context::SlotOffset(variable->index()),
+ result_register(), ecx, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
break;
}
// Assign it.
__ mov(ContextOperand(esi, variable->index()), eax);
// We know that we have written a module, which is not a smi.
- __ RecordWriteContextSlot(esi,
- Context::SlotOffset(variable->index()),
- eax,
- ecx,
- EMIT_REMEMBERED_SET,
+ __ RecordWriteContextSlot(esi, Context::SlotOffset(variable->index()), eax,
+ ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
// Store the subexpression value in the array's elements.
__ mov(FieldOperand(ebx, offset), result_register());
// Update the write barrier for the array store.
- __ RecordWriteField(ebx, offset, result_register(), ecx,
- EMIT_REMEMBERED_SET,
- INLINE_SMI_CHECK);
+ __ RecordWriteField(ebx, offset, result_register(), ecx, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, INLINE_SMI_CHECK);
} else {
// Store the subexpression value in the array's elements.
__ mov(ecx, Immediate(Smi::FromInt(i)));
Immediate(Smi::FromInt(continuation.pos())));
__ mov(FieldOperand(eax, JSGeneratorObject::kContextOffset), esi);
__ mov(ecx, esi);
- __ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx);
+ __ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx,
+ kDontSaveFPRegs);
__ lea(ebx, Operand(ebp, StandardFrameConstants::kExpressionsOffset));
__ cmp(esp, ebx);
__ j(equal, &post_runtime);
Immediate(Smi::FromInt(l_continuation.pos())));
__ mov(FieldOperand(eax, JSGeneratorObject::kContextOffset), esi);
__ mov(ecx, esi);
- __ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx);
+ __ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx,
+ kDontSaveFPRegs);
__ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
__ mov(context_register(),
Operand(ebp, StandardFrameConstants::kContextOffset));
// Only the value field needs a write barrier, as the other values are in the
// root set.
- __ RecordWriteField(eax, JSGeneratorObject::kResultValuePropertyOffset,
- ecx, edx);
+ __ RecordWriteField(eax, JSGeneratorObject::kResultValuePropertyOffset, ecx,
+ edx, kDontSaveFPRegs);
}
if (var->IsContextSlot()) {
__ mov(edx, eax);
int offset = Context::SlotOffset(var->index());
- __ RecordWriteContextSlot(ecx, offset, edx, ebx);
+ __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs);
}
}
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ mov(edx, eax);
- __ RecordWriteField(ebx, JSValue::kValueOffset, edx, ecx);
+ __ RecordWriteField(ebx, JSValue::kValueOffset, edx, ecx, kDontSaveFPRegs);
__ bind(&done);
context()->Plug(eax);
__ mov(Operand(esi, context_offset), eax);
// Update the write barrier. This clobbers eax and ebx.
if (need_write_barrier) {
- __ RecordWriteContextSlot(esi,
- context_offset,
- eax,
- ebx);
+ __ RecordWriteContextSlot(esi, context_offset, eax, ebx,
+ kDontSaveFPRegs);
} else if (FLAG_debug_code) {
Label done;
__ JumpIfInNewSpace(esi, eax, &done, Label::kNear);
Comment(";;; End allocate local context");
}
+ // Initailize FPU state.
+ __ fninit();
// Trace the call.
if (FLAG_trace && info()->IsOptimizing()) {
// We have not executed any compiled code yet, so esi still holds the
int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots();
DCHECK(slots >= 1);
__ sub(esp, Immediate((slots - 1) * kPointerSize));
+
+ // Initailize FPU state.
+ __ fninit();
}
void LCodeGen::GenerateBodyInstructionPost(LInstruction* instr) {
+ // When return from function call, FPU should be initialized again.
+ if (instr->IsCall() && instr->ClobbersDoubleRegisters(isolate())) {
+ bool double_result = instr->HasDoubleRegisterResult();
+ if (double_result) {
+ __ lea(esp, Operand(esp, -kDoubleSize));
+ __ fstp_d(Operand(esp, 0));
+ }
+ __ fninit();
+ if (double_result) {
+ __ fld_d(Operand(esp, 0));
+ __ lea(esp, Operand(esp, kDoubleSize));
+ }
+ }
if (instr->IsGoto()) {
- x87_stack_.LeavingBlock(current_block_, LGoto::cast(instr));
+ x87_stack_.LeavingBlock(current_block_, LGoto::cast(instr), this);
} else if (FLAG_debug_code && FLAG_enable_slow_asserts &&
!instr->IsGap() && !instr->IsReturn()) {
if (instr->ClobbersDoubleRegisters(isolate())) {
void LCodeGen::X87LoadForUsage(X87Register reg1, X87Register reg2) {
DCHECK(x87_stack_.Contains(reg1));
DCHECK(x87_stack_.Contains(reg2));
- x87_stack_.Fxch(reg1, 1);
- x87_stack_.Fxch(reg2);
- x87_stack_.pop();
- x87_stack_.pop();
+ if (reg1.is(reg2) && x87_stack_.depth() == 1) {
+ __ fld(x87_stack_.st(reg1));
+ x87_stack_.push(reg1);
+ x87_stack_.pop();
+ x87_stack_.pop();
+ } else {
+ x87_stack_.Fxch(reg1, 1);
+ x87_stack_.Fxch(reg2);
+ x87_stack_.pop();
+ x87_stack_.pop();
+ }
+}
+
+
+int LCodeGen::X87Stack::GetLayout() {
+ int layout = stack_depth_;
+ for (int i = 0; i < stack_depth_; i++) {
+ layout |= (stack_[stack_depth_ - 1 - i].code() << ((i + 1) * 3));
+ }
+
+ return layout;
}
}
+void LCodeGen::X87Mov(X87Register dst, X87Register src, X87OperandType opts) {
+ if (x87_stack_.Contains(dst)) {
+ x87_stack_.Fxch(dst);
+ __ fstp(0);
+ x87_stack_.pop();
+ // Push ST(i) onto the FPU register stack
+ __ fld(x87_stack_.st(src));
+ x87_stack_.push(dst);
+ } else {
+ // Push ST(i) onto the FPU register stack
+ __ fld(x87_stack_.st(src));
+ x87_stack_.push(dst);
+ }
+}
+
+
void LCodeGen::X87Fld(Operand src, X87OperandType opts) {
DCHECK(!src.is_reg_only());
switch (opts) {
case kX87DoubleOperand:
__ fst_d(dst);
break;
+ case kX87FloatOperand:
+ __ fst_s(dst);
+ break;
case kX87IntOperand:
__ fist_s(dst);
break;
}
-void LCodeGen::X87Stack::LeavingBlock(int current_block_id, LGoto* goto_instr) {
- DCHECK(stack_depth_ <= 1);
- // If ever used for new stubs producing two pairs of doubles joined into two
- // phis this assert hits. That situation is not handled, since the two stacks
- // might have st0 and st1 swapped.
- if (current_block_id + 1 != goto_instr->block_id()) {
+void LCodeGen::X87Stack::LeavingBlock(int current_block_id, LGoto* goto_instr,
+ LCodeGen* cgen) {
+ // For going to a joined block, an explicit LClobberDoubles is inserted before
+ // LGoto. Because all used x87 registers are spilled to stack slots. The
+ // ResolvePhis phase of register allocator could guarantee the two input's x87
+ // stacks have the same layout. So don't check stack_depth_ <= 1 here.
+ int goto_block_id = goto_instr->block_id();
+ if (current_block_id + 1 != goto_block_id) {
// If we have a value on the x87 stack on leaving a block, it must be a
// phi input. If the next block we compile is not the join block, we have
// to discard the stack state.
+ // Before discarding the stack state, we need to save it if the "goto block"
+ // has unreachable last predecessor when FLAG_unreachable_code_elimination.
+ if (FLAG_unreachable_code_elimination) {
+ int length = goto_instr->block()->predecessors()->length();
+ bool has_unreachable_last_predecessor = false;
+ for (int i = 0; i < length; i++) {
+ HBasicBlock* block = goto_instr->block()->predecessors()->at(i);
+ if (block->IsUnreachable() &&
+ (block->block_id() + 1) == goto_block_id) {
+ has_unreachable_last_predecessor = true;
+ }
+ }
+ if (has_unreachable_last_predecessor) {
+ if (cgen->x87_stack_map_.find(goto_block_id) ==
+ cgen->x87_stack_map_.end()) {
+ X87Stack* stack = new (cgen->zone()) X87Stack(*this);
+ cgen->x87_stack_map_.insert(std::make_pair(goto_block_id, stack));
+ }
+ }
+ }
+
+ // Discard the stack state.
stack_depth_ = 0;
}
}
// The deoptimizer does not support X87 Registers. But as long as we
// deopt from a stub its not a problem, since we will re-materialize the
// original stub inputs, which can't be double registers.
- DCHECK(info()->IsStub());
+ // DCHECK(info()->IsStub());
if (FLAG_debug_code && FLAG_enable_slow_asserts) {
__ pushfd();
__ VerifyX87StackDepth(x87_stack_.depth());
__ popfd();
}
- for (int i = 0; i < x87_stack_.depth(); i++) __ fstp(0);
+
+ // Flush X87 stack in the deoptimizer entry.
}
} else {
translation->StoreInt32Register(reg);
}
+ } else if (op->IsDoubleRegister()) {
+ X87Register reg = ToX87Register(op);
+ translation->StoreDoubleRegister(reg);
} else if (op->IsConstantOperand()) {
HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op));
int src_index = DefineDeoptimizationLiteral(constant->handle(isolate()));
}
-void LCodeGen::CallRuntime(const Runtime::Function* fun,
- int argc,
- LInstruction* instr) {
+void LCodeGen::CallRuntime(const Runtime::Function* fun, int argc,
+ LInstruction* instr, SaveFPRegsMode save_doubles) {
DCHECK(instr != NULL);
DCHECK(instr->HasPointerMap());
- __ CallRuntime(fun, argc);
+ __ CallRuntime(fun, argc, save_doubles);
RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT);
LOperand* context) {
LoadContextFromDeferred(context);
- __ CallRuntime(id);
+ __ CallRuntimeSaveDoubles(id);
RecordSafepointWithRegisters(
instr->pointer_map(), argc, Safepoint::kNoLazyDeopt);
__ pop(eax);
__ popfd();
DCHECK(frame_is_built_);
+ // Put the x87 stack layout in TOS.
+ if (x87_stack_.depth() > 0) EmitFlushX87ForDeopt();
+ __ push(Immediate(x87_stack_.GetLayout()));
+ __ fild_s(MemOperand(esp, 0));
+ // Don't touch eflags.
+ __ lea(esp, Operand(esp, kPointerSize));
__ call(entry, RelocInfo::RUNTIME_ENTRY);
__ bind(&no_deopt);
__ mov(Operand::StaticVariable(count), eax);
__ popfd();
}
- // Before Instructions which can deopt, we normally flush the x87 stack. But
- // we can have inputs or outputs of the current instruction on the stack,
- // thus we need to flush them here from the physical stack to leave it in a
- // consistent state.
- if (x87_stack_.depth() > 0) {
+ // Put the x87 stack layout in TOS, so that we can save x87 fp registers in
+ // the correct location.
+ {
Label done;
if (cc != no_condition) __ j(NegateCondition(cc), &done, Label::kNear);
- EmitFlushX87ForDeopt();
+ if (x87_stack_.depth() > 0) EmitFlushX87ForDeopt();
+
+ int x87_stack_layout = x87_stack_.GetLayout();
+ __ push(Immediate(x87_stack_layout));
+ __ fild_s(MemOperand(esp, 0));
+ // Don't touch eflags.
+ __ lea(esp, Operand(esp, kPointerSize));
__ bind(&done);
}
LabelType(label));
__ bind(label->label());
current_block_ = label->block_id();
+ if (label->block()->predecessors()->length() > 1) {
+ // A join block's x87 stack is that of its last visited predecessor.
+ // If the last visited predecessor block is unreachable, the stack state
+ // will be wrong. In such case, use the x87 stack of reachable predecessor.
+ X87StackMap::const_iterator it = x87_stack_map_.find(current_block_);
+ // Restore x87 stack.
+ if (it != x87_stack_map_.end()) {
+ x87_stack_ = *(it->second);
+ }
+ }
DoGap(label);
}
// Bail out if the result is supposed to be negative zero.
Label done;
__ test(left, Operand(left));
- __ j(not_zero, &done, Label::kNear);
+ __ j(not_zero, &done);
if (right->IsConstantOperand()) {
if (ToInteger32(LConstantOperand::cast(right)) < 0) {
DeoptimizeIf(no_condition, instr);
}
__ bind(&return_left);
} else {
- // TODO(weiliang) use X87 for double representation.
- UNIMPLEMENTED();
+ DCHECK(instr->hydrogen()->representation().IsDouble());
+ Label check_nan_left, check_zero, return_left, return_right;
+ Condition condition = (operation == HMathMinMax::kMathMin) ? below : above;
+ X87Register left_reg = ToX87Register(left);
+ X87Register right_reg = ToX87Register(right);
+
+ X87PrepareBinaryOp(left_reg, right_reg, ToX87Register(instr->result()));
+ __ fld(1);
+ __ fld(1);
+ __ FCmp();
+ __ j(parity_even, &check_nan_left, Label::kNear); // At least one NaN.
+ __ j(equal, &check_zero, Label::kNear); // left == right.
+ __ j(condition, &return_left, Label::kNear);
+ __ jmp(&return_right, Label::kNear);
+
+ __ bind(&check_zero);
+ __ fld(0);
+ __ fldz();
+ __ FCmp();
+ __ j(not_equal, &return_left, Label::kNear); // left == right != 0.
+ // At this point, both left and right are either 0 or -0.
+ if (operation == HMathMinMax::kMathMin) {
+ // Push st0 and st1 to stack, then pop them to temp registers and OR them,
+ // load it to left.
+ Register scratch_reg = ToRegister(instr->temp());
+ __ fld(1);
+ __ fld(1);
+ __ sub(esp, Immediate(2 * kPointerSize));
+ __ fstp_s(MemOperand(esp, 0));
+ __ fstp_s(MemOperand(esp, kPointerSize));
+ __ pop(scratch_reg);
+ __ xor_(MemOperand(esp, 0), scratch_reg);
+ X87Mov(left_reg, MemOperand(esp, 0), kX87FloatOperand);
+ __ pop(scratch_reg); // restore esp
+ } else {
+ // Since we operate on +0 and/or -0, addsd and andsd have the same effect.
+ X87Fxch(left_reg);
+ __ fadd(1);
+ }
+ __ jmp(&return_left, Label::kNear);
+
+ __ bind(&check_nan_left);
+ __ fld(0);
+ __ fld(0);
+ __ FCmp(); // NaN check.
+ __ j(parity_even, &return_left, Label::kNear); // left == NaN.
+
+ __ bind(&return_right);
+ X87Fxch(left_reg);
+ X87Mov(left_reg, right_reg);
+
+ __ bind(&return_left);
}
}
UNREACHABLE();
break;
}
+
+ // Only always explicitly storing to memory to force the round-down for double
+ // arithmetic.
+ __ lea(esp, Operand(esp, -kDoubleSize));
+ __ fstp_d(Operand(esp, 0));
+ __ fld_d(Operand(esp, 0));
+ __ lea(esp, Operand(esp, kDoubleSize));
}
__ test(reg, Operand(reg));
EmitBranch(instr, not_zero);
} else if (r.IsDouble()) {
- UNREACHABLE();
+ X87Register reg = ToX87Register(instr->value());
+ X87LoadForUsage(reg);
+ __ fldz();
+ __ FCmp();
+ EmitBranch(instr, not_zero);
} else {
DCHECK(r.IsTagged());
Register reg = ToRegister(instr->value());
DCHECK(!rep.IsInteger32());
if (rep.IsDouble()) {
- UNREACHABLE();
+ X87Register input = ToX87Register(instr->value());
+ X87LoadForUsage(input);
+ __ FXamMinusZero();
+ EmitBranch(instr, equal);
} else {
Register value = ToRegister(instr->value());
Handle<Map> map = masm()->isolate()->factory()->heap_number_map();
? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
Register temp = ToRegister(instr->temp());
int offset = Context::SlotOffset(instr->slot_index());
- __ RecordWriteContextSlot(context,
- offset,
- value,
- temp,
- EMIT_REMEMBERED_SET,
- check_needed);
+ __ RecordWriteContextSlot(context, offset, value, temp, kSaveFPRegs,
+ EMIT_REMEMBERED_SET, check_needed);
}
__ bind(&skip_assignment);
Representation r = instr->hydrogen()->value()->representation();
if (r.IsDouble()) {
- UNIMPLEMENTED();
+ X87Register value = ToX87Register(instr->value());
+ X87Fxch(value);
+ __ fabs();
} else if (r.IsSmiOrInteger32()) {
EmitIntegerMathAbs(instr);
} else { // Tagged case.
void LCodeGen::DoMathFloor(LMathFloor* instr) {
- UNIMPLEMENTED();
+ Register output_reg = ToRegister(instr->result());
+ X87Register input_reg = ToX87Register(instr->value());
+ X87Fxch(input_reg);
+
+ Label not_minus_zero, done;
+ // Deoptimize on unordered.
+ __ fldz();
+ __ fld(1);
+ __ FCmp();
+ DeoptimizeIf(parity_even, instr);
+ __ j(below, ¬_minus_zero, Label::kNear);
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Check for negative zero.
+ __ j(not_equal, ¬_minus_zero, Label::kNear);
+ // +- 0.0.
+ __ fld(0);
+ __ FXamSign();
+ DeoptimizeIf(not_zero, instr);
+ __ Move(output_reg, Immediate(0));
+ __ jmp(&done, Label::kFar);
+ }
+
+ // Positive input.
+ // rc=01B, round down.
+ __ bind(¬_minus_zero);
+ __ fnclex();
+ __ X87SetRC(0x0400);
+ __ sub(esp, Immediate(kPointerSize));
+ __ fist_s(Operand(esp, 0));
+ __ pop(output_reg);
+ __ X87CheckIA();
+ DeoptimizeIf(equal, instr);
+ __ fnclex();
+ __ X87SetRC(0x0000);
+ __ bind(&done);
}
void LCodeGen::DoMathRound(LMathRound* instr) {
- UNIMPLEMENTED();
+ X87Register input_reg = ToX87Register(instr->value());
+ Register result = ToRegister(instr->result());
+ X87Fxch(input_reg);
+ Label below_one_half, below_minus_one_half, done;
+
+ ExternalReference one_half = ExternalReference::address_of_one_half();
+ ExternalReference minus_one_half =
+ ExternalReference::address_of_minus_one_half();
+
+ __ fld_d(Operand::StaticVariable(one_half));
+ __ fld(1);
+ __ FCmp();
+ __ j(carry, &below_one_half);
+
+ // Use rounds towards zero, since 0.5 <= x, we use floor(0.5 + x)
+ __ fld(0);
+ __ fadd_d(Operand::StaticVariable(one_half));
+ // rc=11B, round toward zero.
+ __ X87SetRC(0x0c00);
+ __ sub(esp, Immediate(kPointerSize));
+ // Clear exception bits.
+ __ fnclex();
+ __ fistp_s(MemOperand(esp, 0));
+ // Check overflow.
+ __ X87CheckIA();
+ __ RecordComment("D2I conversion overflow");
+ __ pop(result);
+ DeoptimizeIf(equal, instr);
+ __ fnclex();
+ // Restore round mode.
+ __ X87SetRC(0x0000);
+ __ jmp(&done);
+
+ __ bind(&below_one_half);
+ __ fld_d(Operand::StaticVariable(minus_one_half));
+ __ fld(1);
+ __ FCmp();
+ __ j(carry, &below_minus_one_half);
+ // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if
+ // we can ignore the difference between a result of -0 and +0.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // If the sign is positive, we return +0.
+ __ fld(0);
+ __ FXamSign();
+ __ RecordComment("Minus zero");
+ DeoptimizeIf(not_zero, instr);
+ }
+ __ Move(result, Immediate(0));
+ __ jmp(&done);
+
+ __ bind(&below_minus_one_half);
+ __ fld(0);
+ __ fadd_d(Operand::StaticVariable(one_half));
+ // rc=01B, round down.
+ __ X87SetRC(0x0400);
+ __ sub(esp, Immediate(kPointerSize));
+ // Clear exception bits.
+ __ fnclex();
+ __ fistp_s(MemOperand(esp, 0));
+ // Check overflow.
+ __ X87CheckIA();
+ __ RecordComment("D2I conversion overflow");
+ __ pop(result);
+ DeoptimizeIf(equal, instr);
+ __ fnclex();
+ // Restore round mode.
+ __ X87SetRC(0x0000);
+
+ __ bind(&done);
}
void LCodeGen::DoMathFround(LMathFround* instr) {
- UNIMPLEMENTED();
+ X87Register input_reg = ToX87Register(instr->value());
+ X87Fxch(input_reg);
+ __ sub(esp, Immediate(kPointerSize));
+ __ fstp_s(MemOperand(esp, 0));
+ X87Fld(MemOperand(esp, 0), kX87FloatOperand);
+ __ add(esp, Immediate(kPointerSize));
}
void LCodeGen::DoMathSqrt(LMathSqrt* instr) {
- UNIMPLEMENTED();
+ X87Register input_reg = ToX87Register(instr->value());
+ X87Register output_reg = ToX87Register(instr->result());
+ DCHECK(output_reg.is(input_reg));
+ USE(output_reg);
+ X87Fxch(input_reg);
+ __ fsqrt();
}
void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) {
- UNIMPLEMENTED();
+ X87Register input_reg = ToX87Register(instr->value());
+ DCHECK(ToX87Register(instr->result()).is(input_reg));
+ X87Fxch(input_reg);
+ // Note that according to ECMA-262 15.8.2.13:
+ // Math.pow(-Infinity, 0.5) == Infinity
+ // Math.sqrt(-Infinity) == NaN
+ Label done, sqrt;
+ // Check base for -Infinity. C3 == 0, C2 == 1, C1 == 1 and C0 == 1
+ __ fxam();
+ __ push(eax);
+ __ fnstsw_ax();
+ __ and_(eax, Immediate(0x4700));
+ __ cmp(eax, Immediate(0x0700));
+ __ j(not_equal, &sqrt, Label::kNear);
+ // If input is -Infinity, return Infinity.
+ __ fchs();
+ __ jmp(&done, Label::kNear);
+
+ // Square root.
+ __ bind(&sqrt);
+ __ fldz();
+ __ faddp(); // Convert -0 to +0.
+ __ fsqrt();
+ __ bind(&done);
+ __ pop(eax);
}
void LCodeGen::DoPower(LPower* instr) {
- UNIMPLEMENTED();
+ Representation exponent_type = instr->hydrogen()->right()->representation();
+ X87Register result = ToX87Register(instr->result());
+ // Having marked this as a call, we can use any registers.
+ X87Register base = ToX87Register(instr->left());
+ ExternalReference one_half = ExternalReference::address_of_one_half();
+
+ if (exponent_type.IsSmi()) {
+ Register exponent = ToRegister(instr->right());
+ X87LoadForUsage(base);
+ __ SmiUntag(exponent);
+ __ push(exponent);
+ __ fild_s(MemOperand(esp, 0));
+ __ pop(exponent);
+ } else if (exponent_type.IsTagged()) {
+ Register exponent = ToRegister(instr->right());
+ Register temp = exponent.is(ecx) ? eax : ecx;
+ Label no_deopt, done;
+ X87LoadForUsage(base);
+ __ JumpIfSmi(exponent, &no_deopt);
+ __ CmpObjectType(exponent, HEAP_NUMBER_TYPE, temp);
+ DeoptimizeIf(not_equal, instr);
+ // Heap number(double)
+ __ fld_d(FieldOperand(exponent, HeapNumber::kValueOffset));
+ __ jmp(&done);
+ // SMI
+ __ bind(&no_deopt);
+ __ SmiUntag(exponent);
+ __ push(exponent);
+ __ fild_s(MemOperand(esp, 0));
+ __ pop(exponent);
+ __ bind(&done);
+ } else if (exponent_type.IsInteger32()) {
+ Register exponent = ToRegister(instr->right());
+ X87LoadForUsage(base);
+ __ push(exponent);
+ __ fild_s(MemOperand(esp, 0));
+ __ pop(exponent);
+ } else {
+ DCHECK(exponent_type.IsDouble());
+ X87Register exponent_double = ToX87Register(instr->right());
+ X87LoadForUsage(base, exponent_double);
+ }
+
+ // FP data stack {base, exponent(TOS)}.
+ // Handle (exponent==+-0.5 && base == -0).
+ Label not_plus_0;
+ __ fld(0);
+ __ fabs();
+ X87Fld(Operand::StaticVariable(one_half), kX87DoubleOperand);
+ __ FCmp();
+ __ j(parity_even, ¬_plus_0, Label::kNear); // NaN.
+ __ j(not_equal, ¬_plus_0, Label::kNear);
+ __ fldz();
+ // FP data stack {base, exponent(TOS), zero}.
+ __ faddp(2);
+ __ bind(¬_plus_0);
+
+ {
+ __ PrepareCallCFunction(4, eax);
+ __ fstp_d(MemOperand(esp, kDoubleSize)); // Exponent value.
+ __ fstp_d(MemOperand(esp, 0)); // Base value.
+ X87PrepareToWrite(result);
+ __ CallCFunction(ExternalReference::power_double_double_function(isolate()),
+ 4);
+ // Return value is in st(0) on ia32.
+ X87CommitWrite(result);
+ }
}
void LCodeGen::DoMathLog(LMathLog* instr) {
- UNIMPLEMENTED();
+ DCHECK(instr->value()->Equals(instr->result()));
+ X87Register input_reg = ToX87Register(instr->value());
+ X87Fxch(input_reg);
+
+ Label positive, done, zero, nan_result;
+ __ fldz();
+ __ fld(1);
+ __ FCmp();
+ __ j(below, &nan_result, Label::kNear);
+ __ j(equal, &zero, Label::kNear);
+ // Positive input.
+ // {input, ln2}.
+ __ fldln2();
+ // {ln2, input}.
+ __ fxch();
+ // {result}.
+ __ fyl2x();
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&nan_result);
+ ExternalReference nan =
+ ExternalReference::address_of_canonical_non_hole_nan();
+ X87PrepareToWrite(input_reg);
+ __ fld_d(Operand::StaticVariable(nan));
+ X87CommitWrite(input_reg);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&zero);
+ ExternalReference ninf = ExternalReference::address_of_negative_infinity();
+ X87PrepareToWrite(input_reg);
+ __ fld_d(Operand::StaticVariable(ninf));
+ X87CommitWrite(input_reg);
+
+ __ bind(&done);
}
void LCodeGen::DoMathClz32(LMathClz32* instr) {
- UNIMPLEMENTED();
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ Label not_zero_input;
+ __ bsr(result, input);
+
+ __ j(not_zero, ¬_zero_input);
+ __ Move(result, Immediate(63)); // 63^31 == 32
+
+ __ bind(¬_zero_input);
+ __ xor_(result, Immediate(31)); // for x in [0..31], 31^x == 31-x.
}
void LCodeGen::DoMathExp(LMathExp* instr) {
- UNIMPLEMENTED();
+ X87Register input = ToX87Register(instr->value());
+ X87Register result_reg = ToX87Register(instr->result());
+ Register temp_result = ToRegister(instr->temp1());
+ Register temp = ToRegister(instr->temp2());
+ Label slow, done, smi, finish;
+ DCHECK(result_reg.is(input));
+
+ // Store input into Heap number and call runtime function kMathExpRT.
+ if (FLAG_inline_new) {
+ __ AllocateHeapNumber(temp_result, temp, no_reg, &slow);
+ __ jmp(&done, Label::kNear);
+ }
+
+ // Slow case: Call the runtime system to do the number allocation.
+ __ bind(&slow);
+ {
+ // TODO(3095996): Put a valid pointer value in the stack slot where the
+ // result register is stored, as this register is in the pointer map, but
+ // contains an integer value.
+ __ Move(temp_result, Immediate(0));
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
+ RecordSafepointWithRegisters(instr->pointer_map(), 0,
+ Safepoint::kNoLazyDeopt);
+ __ StoreToSafepointRegisterSlot(temp_result, eax);
+ }
+ __ bind(&done);
+ X87LoadForUsage(input);
+ __ fstp_d(FieldOperand(temp_result, HeapNumber::kValueOffset));
+
+ {
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ push(temp_result);
+ __ CallRuntimeSaveDoubles(Runtime::kMathExpRT);
+ RecordSafepointWithRegisters(instr->pointer_map(), 0,
+ Safepoint::kNoLazyDeopt);
+ __ StoreToSafepointRegisterSlot(temp_result, eax);
+ }
+ X87PrepareToWrite(result_reg);
+ // return value of MathExpRT is Smi or Heap Number.
+ __ JumpIfSmi(temp_result, &smi);
+ // Heap number(double)
+ __ fld_d(FieldOperand(temp_result, HeapNumber::kValueOffset));
+ __ jmp(&finish);
+ // SMI
+ __ bind(&smi);
+ __ SmiUntag(temp_result);
+ __ push(temp_result);
+ __ fild_s(MemOperand(esp, 0));
+ __ pop(temp_result);
+ __ bind(&finish);
+ X87CommitWrite(result_reg);
}
void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
DCHECK(ToRegister(instr->context()).is(esi));
- CallRuntime(instr->function(), instr->arity(), instr);
+ CallRuntime(instr->function(), instr->arity(), instr, instr->save_doubles());
}
__ mov(temp_map, transition);
__ mov(FieldOperand(object, HeapObject::kMapOffset), temp_map);
// Update the write barrier for the map field.
- __ RecordWriteForMap(object, transition, temp_map, temp);
+ __ RecordWriteForMap(object, transition, temp_map, temp, kSaveFPRegs);
}
}
Register value = ToRegister(instr->value());
Register temp = access.IsInobject() ? ToRegister(instr->temp()) : object;
// Update the write barrier for the object for in-object properties.
- __ RecordWriteField(write_register,
- offset,
- value,
- temp,
+ __ RecordWriteField(write_register, offset, value, temp, kSaveFPRegs,
EMIT_REMEMBERED_SET,
instr->hydrogen()->SmiCheckForWriteBarrier(),
instr->hydrogen()->PointersToHereCheckForValue());
instr->base_offset()));
if (elements_kind == EXTERNAL_FLOAT32_ELEMENTS ||
elements_kind == FLOAT32_ELEMENTS) {
- __ fld(0);
- __ fstp_s(operand);
+ X87Mov(operand, ToX87Register(instr->value()), kX87FloatOperand);
} else if (elements_kind == EXTERNAL_FLOAT64_ELEMENTS ||
elements_kind == FLOAT64_ELEMENTS) {
X87Mov(operand, ToX87Register(instr->value()));
? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
// Compute address of modified element and store it into key register.
__ lea(key, operand);
- __ RecordWrite(elements,
- key,
- value,
- EMIT_REMEMBERED_SET,
+ __ RecordWrite(elements, key, value, kSaveFPRegs, EMIT_REMEMBERED_SET,
check_needed,
instr->hydrogen()->PointersToHereCheckForValue());
}
// Write barrier.
DCHECK_NE(instr->temp(), NULL);
__ RecordWriteForMap(object_reg, to_map, new_map_reg,
- ToRegister(instr->temp()));
+ ToRegister(instr->temp()), kDontSaveFPRegs);
} else {
DCHECK(ToRegister(instr->context()).is(esi));
DCHECK(object_reg.is(eax));
// The corresponding HChange instructions are added in a phase that does
// not have easy access to the local context.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntime(Runtime::kAllocateHeapNumber);
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
RecordSafepointWithRegisters(
instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
__ StoreToSafepointRegisterSlot(reg, eax);
// Put the value to the top of stack
X87Register src = ToX87Register(instr->value());
- X87LoadForUsage(src);
+ // Don't use X87LoadForUsage here, which is only used by Instruction which
+ // clobbers fp registers.
+ x87_stack_.Fxch(src);
DeferredNumberTagD* deferred =
new(zone()) DeferredNumberTagD(this, instr, x87_stack_);
__ jmp(deferred->entry());
}
__ bind(deferred->exit());
- __ fstp_d(FieldOperand(reg, HeapNumber::kValueOffset));
+ __ fst_d(FieldOperand(reg, HeapNumber::kValueOffset));
}
// The corresponding HChange instructions are added in a phase that does
// not have easy access to the local context.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntime(Runtime::kAllocateHeapNumber);
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
RecordSafepointWithRegisters(
instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
__ StoreToSafepointRegisterSlot(reg, eax);
X87PrepareToWrite(res_reg);
if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) {
// Smi check.
- __ JumpIfSmi(input_reg, &load_smi, Label::kNear);
+ __ JumpIfSmi(input_reg, &load_smi);
// Heap number map check.
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
DeoptimizeIf(not_equal, instr);
} else {
Label heap_number, convert;
- __ j(equal, &heap_number, Label::kNear);
+ __ j(equal, &heap_number);
// Convert undefined (or hole) to NaN.
__ cmp(input_reg, factory()->undefined_value());
PushSafepointRegistersScope scope(this);
__ push(object);
__ xor_(esi, esi);
- __ CallRuntime(Runtime::kTryMigrateInstance);
+ __ CallRuntimeSaveDoubles(Runtime::kTryMigrateInstance);
RecordSafepointWithRegisters(
instr->pointer_map(), 1, Safepoint::kNoLazyDeopt);
void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
- UNREACHABLE();
+ X87Register value_reg = ToX87Register(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ X87Fxch(value_reg);
+ __ ClampTOSToUint8(result_reg);
}
void LCodeGen::DoDoubleBits(LDoubleBits* instr) {
- UNREACHABLE();
+ X87Register value_reg = ToX87Register(instr->value());
+ Register result_reg = ToRegister(instr->result());
+ X87Fxch(value_reg);
+ __ sub(esp, Immediate(kDoubleSize));
+ __ fst_d(Operand(esp, 0));
+ if (instr->hydrogen()->bits() == HDoubleBits::HIGH) {
+ __ mov(result_reg, Operand(esp, kPointerSize));
+ } else {
+ __ mov(result_reg, Operand(esp, 0));
+ }
+ __ add(esp, Immediate(kDoubleSize));
}
void LCodeGen::DoConstructDouble(LConstructDouble* instr) {
- UNREACHABLE();
+ Register hi_reg = ToRegister(instr->hi());
+ Register lo_reg = ToRegister(instr->lo());
+ X87Register result_reg = ToX87Register(instr->result());
+ // Follow below pattern to write a x87 fp register.
+ X87PrepareToWrite(result_reg);
+ __ sub(esp, Immediate(kDoubleSize));
+ __ mov(Operand(esp, 0), lo_reg);
+ __ mov(Operand(esp, kPointerSize), hi_reg);
+ __ fld_d(Operand(esp, 0));
+ __ add(esp, Immediate(kDoubleSize));
+ X87CommitWrite(result_reg);
}
void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
PushSafepointRegistersScope scope(this);
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntime(Runtime::kStackGuard);
+ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
RecordSafepointWithLazyDeopt(
instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
DCHECK(instr->HasEnvironment());
__ push(object);
__ push(index);
__ xor_(esi, esi);
- __ CallRuntime(Runtime::kLoadMutableDouble);
+ __ CallRuntimeSaveDoubles(Runtime::kLoadMutableDouble);
RecordSafepointWithRegisters(
instr->pointer_map(), 2, Safepoint::kNoLazyDeopt);
__ StoreToSafepointRegisterSlot(object, eax);
#ifndef V8_X87_LITHIUM_CODEGEN_X87_H_
#define V8_X87_LITHIUM_CODEGEN_X87_H_
+#include <map>
#include "src/x87/lithium-x87.h"
#include "src/base/logging.h"
X87OperandType operand = kX87DoubleOperand);
void X87Mov(Operand src, X87Register reg,
X87OperandType operand = kX87DoubleOperand);
+ void X87Mov(X87Register reg, X87Register src,
+ X87OperandType operand = kX87DoubleOperand);
void X87PrepareBinaryOp(
X87Register left, X87Register right, X87Register result);
LInstruction* instr,
SafepointMode safepoint_mode);
- void CallRuntime(const Runtime::Function* fun,
- int argc,
- LInstruction* instr);
+ void CallRuntime(const Runtime::Function* fun, int argc, LInstruction* instr,
+ SaveFPRegsMode save_doubles = kDontSaveFPRegs);
void CallRuntime(Runtime::FunctionId id,
int argc,
int osr_pc_offset_;
bool frame_is_built_;
- class X87Stack {
+ class X87Stack : public ZoneObject {
public:
explicit X87Stack(MacroAssembler* masm)
: stack_depth_(0), is_mutable_(true), masm_(masm) { }
}
return true;
}
+ X87Stack& operator=(const X87Stack& other) {
+ stack_depth_ = other.stack_depth_;
+ for (int i = 0; i < stack_depth_; i++) {
+ stack_[i] = other.stack_[i];
+ }
+ return *this;
+ }
bool Contains(X87Register reg);
void Fxch(X87Register reg, int other_slot = 0);
void Free(X87Register reg);
void PrepareToWrite(X87Register reg);
void CommitWrite(X87Register reg);
void FlushIfNecessary(LInstruction* instr, LCodeGen* cgen);
- void LeavingBlock(int current_block_id, LGoto* goto_instr);
+ void LeavingBlock(int current_block_id, LGoto* goto_instr, LCodeGen* cgen);
int depth() const { return stack_depth_; }
+ int GetLayout();
+ int st(X87Register reg) { return st2idx(ArrayIndex(reg)); }
void pop() {
DCHECK(is_mutable_);
stack_depth_--;
MacroAssembler* masm_;
};
X87Stack x87_stack_;
+ // block_id -> X87Stack*;
+ typedef std::map<int, X87Stack*> X87StackMap;
+ X87StackMap x87_stack_map_;
// Builder that keeps track of safepoints in the code. The table
// itself is emitted at the end of the generated code.
friend class LDeferredCode;
friend class LEnvironment;
friend class SafepointGenerator;
+ friend class X87Stack;
DISALLOW_COPY_AND_ASSIGN(LCodeGen);
};
} else if (source->IsDoubleRegister()) {
// load from the register onto the stack, store in destination, which must
// be a double stack slot in the non-SSE2 case.
- DCHECK(destination->IsDoubleStackSlot());
- Operand dst = cgen_->ToOperand(destination);
- X87Register src = cgen_->ToX87Register(source);
- cgen_->X87Mov(dst, src);
+ if (destination->IsDoubleStackSlot()) {
+ Operand dst = cgen_->ToOperand(destination);
+ X87Register src = cgen_->ToX87Register(source);
+ cgen_->X87Mov(dst, src);
+ } else {
+ X87Register dst = cgen_->ToX87Register(destination);
+ X87Register src = cgen_->ToX87Register(source);
+ cgen_->X87Mov(dst, src);
+ }
} else if (source->IsDoubleStackSlot()) {
// load from the stack slot on top of the floating point stack, and then
// store in destination. If destination is a double register, then it
}
+LUnallocated* LChunkBuilder::ToUnallocated(X87Register reg) {
+ return new (zone()) LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER,
+ X87Register::ToAllocationIndex(reg));
+}
+
+
LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) {
return Use(value, ToUnallocated(fixed_register));
}
}
+LInstruction* LChunkBuilder::DefineFixed(LTemplateResultInstruction<1>* instr,
+ X87Register reg) {
+ return Define(instr, ToUnallocated(reg));
+}
+
+
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0;
if (current->IsControlInstruction() &&
HControlInstruction::cast(current)->KnownSuccessorBlock(&successor) &&
successor != NULL) {
+ // Always insert a fpu register barrier here when branch is optimized to
+ // be a direct goto.
+ // TODO(weiliang): require a better solution.
+ if (!current->IsGoto()) {
+ LClobberDoubles* clobber = new (zone()) LClobberDoubles(isolate());
+ clobber->set_hydrogen_value(current);
+ chunk_->AddInstruction(clobber, current_block_);
+ }
instr = new(zone()) LGoto(successor);
} else {
instr = current->CompileToLithium(this);
if (FLAG_stress_environments && !instr->HasEnvironment()) {
instr = AssignEnvironment(instr);
}
- if (instr->IsGoto() && LGoto::cast(instr)->jumps_to_join()) {
+ if (instr->IsGoto() &&
+ (LGoto::cast(instr)->jumps_to_join() || next_block_->is_osr_entry())) {
// TODO(olivf) Since phis of spilled values are joined as registers
// (not in the stack slot), we need to allow the goto gaps to keep one
// x87 register alive. To ensure all other values are still spilled, we
bool easy_case = !r.IsTagged() || type.IsBoolean() || type.IsSmi() ||
type.IsJSArray() || type.IsHeapNumber() || type.IsString();
LOperand* temp = !easy_case && expected.NeedsMap() ? TempRegister() : NULL;
- LInstruction* branch = new(zone()) LBranch(UseRegister(value), temp);
+ LInstruction* branch =
+ temp != NULL ? new (zone()) LBranch(UseRegister(value), temp)
+ : new (zone()) LBranch(UseRegisterAtStart(value), temp);
if (!easy_case &&
((!expected.Contains(ToBooleanStub::SMI) && expected.NeedsMap()) ||
!expected.IsGeneric())) {
LInstruction* LChunkBuilder::DoMathRound(HUnaryMathOperation* instr) {
- // Crankshaft is turned off for nosse2.
- UNREACHABLE();
- return NULL;
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LInstruction* result = DefineAsRegister(new (zone()) LMathRound(input));
+ return AssignEnvironment(result);
}
LInstruction* LChunkBuilder::DoMathFround(HUnaryMathOperation* instr) {
- LOperand* input = UseRegisterAtStart(instr->value());
+ LOperand* input = UseRegister(instr->value());
LMathFround* result = new (zone()) LMathFround(input);
- return AssignEnvironment(DefineAsRegister(result));
+ return DefineSameAsFirst(result);
}
LInstruction* LChunkBuilder::DoMathExp(HUnaryMathOperation* instr) {
DCHECK(instr->representation().IsDouble());
DCHECK(instr->value()->representation().IsDouble());
- LOperand* value = UseTempRegister(instr->value());
- LOperand* temp1 = TempRegister();
- LOperand* temp2 = TempRegister();
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp1 = FixedTemp(ecx);
+ LOperand* temp2 = FixedTemp(edx);
LMathExp* result = new(zone()) LMathExp(value, temp1, temp2);
- return DefineAsRegister(result);
+ return MarkAsCall(DefineSameAsFirst(result), instr);
}
LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) {
LOperand* input = UseRegisterAtStart(instr->value());
- LOperand* temp = TempRegister();
- LMathPowHalf* result = new(zone()) LMathPowHalf(input, temp);
+ LMathPowHalf* result = new (zone()) LMathPowHalf(input);
return DefineSameAsFirst(result);
}
LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
LOperand* left = NULL;
LOperand* right = NULL;
+ LOperand* scratch = TempRegister();
+
if (instr->representation().IsSmiOrInteger32()) {
DCHECK(instr->left()->representation().Equals(instr->representation()));
DCHECK(instr->right()->representation().Equals(instr->representation()));
left = UseRegisterAtStart(instr->left());
right = UseRegisterAtStart(instr->right());
}
- LMathMinMax* minmax = new(zone()) LMathMinMax(left, right);
+ LMathMinMax* minmax = new (zone()) LMathMinMax(left, right, scratch);
return DefineSameAsFirst(minmax);
}
LInstruction* LChunkBuilder::DoPower(HPower* instr) {
- // Crankshaft is turned off for nosse2.
- UNREACHABLE();
- return NULL;
+ // Unlike ia32, we don't have a MathPowStub and directly call c function.
+ DCHECK(instr->representation().IsDouble());
+ DCHECK(instr->left()->representation().IsDouble());
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ LPower* result = new (zone()) LPower(left, right);
+ return MarkAsCall(DefineSameAsFirst(result), instr);
}
LInstruction* LChunkBuilder::DoCompareMinusZeroAndBranch(
HCompareMinusZeroAndBranch* instr) {
- LOperand* value = UseRegister(instr->value());
- LOperand* scratch = TempRegister();
- return new(zone()) LCompareMinusZeroAndBranch(value, scratch);
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new (zone()) LCompareMinusZeroAndBranch(value);
}
HValue* value = instr->value();
Representation input_rep = value->representation();
if (input_rep.IsDouble()) {
- UNREACHABLE();
- return NULL;
+ LOperand* reg = UseRegister(value);
+ return DefineFixed(new (zone()) LClampDToUint8(reg), eax);
} else if (input_rep.IsInteger32()) {
LOperand* reg = UseFixed(value, eax);
return DefineFixed(new(zone()) LClampIToUint8(reg), eax);
}
bool jumps_to_join() const { return block_->predecessors()->length() > 1; }
+ HBasicBlock* block() const { return block_; }
private:
HBasicBlock* block_;
};
-class LMathPowHalf FINAL : public LTemplateInstruction<1, 1, 1> {
+class LMathPowHalf FINAL : public LTemplateInstruction<1, 1, 0> {
public:
- LMathPowHalf(LOperand* value, LOperand* temp) {
- inputs_[0] = value;
- temps_[0] = temp;
- }
+ explicit LMathPowHalf(LOperand* value) { inputs_[0] = value; }
LOperand* value() { return inputs_[0]; }
- LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half")
};
};
-class LCompareMinusZeroAndBranch FINAL : public LControlInstruction<1, 1> {
+class LCompareMinusZeroAndBranch FINAL : public LControlInstruction<1, 0> {
public:
- LCompareMinusZeroAndBranch(LOperand* value, LOperand* temp) {
- inputs_[0] = value;
- temps_[0] = temp;
- }
+ explicit LCompareMinusZeroAndBranch(LOperand* value) { inputs_[0] = value; }
LOperand* value() { return inputs_[0]; }
- LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch,
"cmp-minus-zero-and-branch")
};
-class LMathMinMax FINAL : public LTemplateInstruction<1, 2, 0> {
+class LMathMinMax FINAL : public LTemplateInstruction<1, 2, 1> {
public:
- LMathMinMax(LOperand* left, LOperand* right) {
+ LMathMinMax(LOperand* left, LOperand* right, LOperand* temp) {
inputs_[0] = left;
inputs_[1] = right;
+ temps_[0] = temp;
}
LOperand* left() { return inputs_[0]; }
LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "math-min-max")
DECLARE_HYDROGEN_ACCESSOR(MathMinMax)
DECLARE_HYDROGEN_ACCESSOR(CallRuntime)
virtual bool ClobbersDoubleRegisters(Isolate* isolate) const OVERRIDE {
- return true;
+ return save_doubles() == kDontSaveFPRegs;
}
const Runtime::Function* function() const { return hydrogen()->function(); }
int arity() const { return hydrogen()->argument_count(); }
+ SaveFPRegsMode save_doubles() const { return hydrogen()->save_doubles(); }
};
LInstruction* DefineSameAsFirst(LTemplateResultInstruction<1>* instr);
LInstruction* DefineFixed(LTemplateResultInstruction<1>* instr,
Register reg);
+ LInstruction* DefineFixed(LTemplateResultInstruction<1>* instr,
+ X87Register reg);
LInstruction* DefineX87TOS(LTemplateResultInstruction<1>* instr);
// Assigns an environment to an instruction. An instruction which can
// deoptimize must have an environment.
void MacroAssembler::RememberedSetHelper(
Register object, // Only used for debug checks.
- Register addr,
- Register scratch,
+ Register addr, Register scratch, SaveFPRegsMode save_fp,
MacroAssembler::RememberedSetFinalAction and_then) {
Label done;
if (emit_debug_code()) {
DCHECK(and_then == kFallThroughAtEnd);
j(equal, &done, Label::kNear);
}
- StoreBufferOverflowStub store_buffer_overflow(isolate(), kDontSaveFPRegs);
+ StoreBufferOverflowStub store_buffer_overflow(isolate(), save_fp);
CallStub(&store_buffer_overflow);
if (and_then == kReturnAtEnd) {
ret(0);
}
+void MacroAssembler::ClampTOSToUint8(Register result_reg) {
+ Label done, conv_failure;
+ sub(esp, Immediate(kPointerSize));
+ fnclex();
+ fist_s(Operand(esp, 0));
+ pop(result_reg);
+ X87CheckIA();
+ j(equal, &conv_failure, Label::kNear);
+ test(result_reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ setcc(sign, result_reg);
+ sub(result_reg, Immediate(1));
+ and_(result_reg, Immediate(255));
+ jmp(&done, Label::kNear);
+ bind(&conv_failure);
+ fnclex();
+ fldz();
+ fld(1);
+ FCmp();
+ setcc(below, result_reg); // 1 if negative, 0 if positive.
+ dec_b(result_reg); // 0 if negative, 255 if positive.
+ bind(&done);
+}
+
+
void MacroAssembler::ClampUint8(Register reg) {
Label done;
test(reg, Immediate(0xFFFFFF00));
void MacroAssembler::RecordWriteArray(
- Register object,
- Register value,
- Register index,
- RememberedSetAction remembered_set_action,
- SmiCheck smi_check,
+ Register object, Register value, Register index, SaveFPRegsMode save_fp,
+ RememberedSetAction remembered_set_action, SmiCheck smi_check,
PointersToHereCheck pointers_to_here_check_for_value) {
// First, check if a write barrier is even needed. The tests below
// catch stores of Smis.
lea(dst, Operand(object, index, times_half_pointer_size,
FixedArray::kHeaderSize - kHeapObjectTag));
- RecordWrite(object, dst, value, remembered_set_action, OMIT_SMI_CHECK,
- pointers_to_here_check_for_value);
+ RecordWrite(object, dst, value, save_fp, remembered_set_action,
+ OMIT_SMI_CHECK, pointers_to_here_check_for_value);
bind(&done);
void MacroAssembler::RecordWriteField(
- Register object,
- int offset,
- Register value,
- Register dst,
- RememberedSetAction remembered_set_action,
- SmiCheck smi_check,
- PointersToHereCheck pointers_to_here_check_for_value) {
+ Register object, int offset, Register value, Register dst,
+ SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action,
+ SmiCheck smi_check, PointersToHereCheck pointers_to_here_check_for_value) {
// First, check if a write barrier is even needed. The tests below
// catch stores of Smis.
Label done;
bind(&ok);
}
- RecordWrite(object, dst, value, remembered_set_action, OMIT_SMI_CHECK,
- pointers_to_here_check_for_value);
+ RecordWrite(object, dst, value, save_fp, remembered_set_action,
+ OMIT_SMI_CHECK, pointers_to_here_check_for_value);
bind(&done);
}
-void MacroAssembler::RecordWriteForMap(
- Register object,
- Handle<Map> map,
- Register scratch1,
- Register scratch2) {
+void MacroAssembler::RecordWriteForMap(Register object, Handle<Map> map,
+ Register scratch1, Register scratch2,
+ SaveFPRegsMode save_fp) {
Label done;
Register address = scratch1;
&done,
Label::kNear);
- RecordWriteStub stub(isolate(), object, value, address, OMIT_REMEMBERED_SET);
+ RecordWriteStub stub(isolate(), object, value, address, OMIT_REMEMBERED_SET,
+ save_fp);
CallStub(&stub);
bind(&done);
void MacroAssembler::RecordWrite(
- Register object,
- Register address,
- Register value,
- RememberedSetAction remembered_set_action,
- SmiCheck smi_check,
+ Register object, Register address, Register value, SaveFPRegsMode fp_mode,
+ RememberedSetAction remembered_set_action, SmiCheck smi_check,
PointersToHereCheck pointers_to_here_check_for_value) {
DCHECK(!object.is(value));
DCHECK(!object.is(address));
&done,
Label::kNear);
- RecordWriteStub stub(isolate(), object, value, address,
- remembered_set_action);
+ RecordWriteStub stub(isolate(), object, value, address, remembered_set_action,
+ fp_mode);
CallStub(&stub);
bind(&done);
}
+void MacroAssembler::FXamMinusZero() {
+ fxam();
+ push(eax);
+ fnstsw_ax();
+ and_(eax, Immediate(0x4700));
+ // For minus zero, C3 == 1 && C1 == 1.
+ cmp(eax, Immediate(0x4200));
+ pop(eax);
+ fstp(0);
+}
+
+
+void MacroAssembler::FXamSign() {
+ fxam();
+ push(eax);
+ fnstsw_ax();
+ // For negative value (including -0.0), C1 == 1.
+ and_(eax, Immediate(0x0200));
+ pop(eax);
+ fstp(0);
+}
+
+
+void MacroAssembler::X87CheckIA() {
+ push(eax);
+ fnstsw_ax();
+ // For #IA, IE == 1 && SF == 0.
+ and_(eax, Immediate(0x0041));
+ cmp(eax, Immediate(0x0001));
+ pop(eax);
+}
+
+
+// rc=00B, round to nearest.
+// rc=01B, round down.
+// rc=10B, round up.
+// rc=11B, round toward zero.
+void MacroAssembler::X87SetRC(int rc) {
+ sub(esp, Immediate(kPointerSize));
+ fnstcw(MemOperand(esp, 0));
+ and_(MemOperand(esp, 0), Immediate(0xF3FF));
+ or_(MemOperand(esp, 0), Immediate(rc));
+ fldcw(MemOperand(esp, 0));
+ add(esp, Immediate(kPointerSize));
+}
+
+
void MacroAssembler::AssertNumber(Register object) {
if (emit_debug_code()) {
Label ok;
}
-void MacroAssembler::EnterExitFrameEpilogue(int argc) {
- sub(esp, Immediate(argc * kPointerSize));
+void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) {
+ // Optionally save FPU state.
+ if (save_doubles) {
+ // Store FPU state to m108byte.
+ int space = 108 + argc * kPointerSize;
+ sub(esp, Immediate(space));
+ const int offset = -2 * kPointerSize; // entry fp + code object.
+ fnsave(MemOperand(ebp, offset - 108));
+ } else {
+ sub(esp, Immediate(argc * kPointerSize));
+ }
// Get the required frame alignment for the OS.
const int kFrameAlignment = base::OS::ActivationFrameAlignment();
}
-void MacroAssembler::EnterExitFrame() {
+void MacroAssembler::EnterExitFrame(bool save_doubles) {
EnterExitFramePrologue();
// Set up argc and argv in callee-saved registers.
lea(esi, Operand(ebp, eax, times_4, offset));
// Reserve space for argc, argv and isolate.
- EnterExitFrameEpilogue(3);
+ EnterExitFrameEpilogue(3, save_doubles);
}
void MacroAssembler::EnterApiExitFrame(int argc) {
EnterExitFramePrologue();
- EnterExitFrameEpilogue(argc);
+ EnterExitFrameEpilogue(argc, false);
}
-void MacroAssembler::LeaveExitFrame() {
+void MacroAssembler::LeaveExitFrame(bool save_doubles) {
+ // Optionally restore FPU state.
+ if (save_doubles) {
+ const int offset = -2 * kPointerSize;
+ frstor(MemOperand(ebp, offset - 108));
+ }
+
// Get the return address from the stack and restore the frame pointer.
mov(ecx, Operand(ebp, 1 * kPointerSize));
mov(ebp, Operand(ebp, 0 * kPointerSize));
}
-void MacroAssembler::CallRuntime(const Runtime::Function* f,
- int num_arguments) {
+void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
+ SaveFPRegsMode save_doubles) {
// If the expected number of arguments of the runtime function is
// constant, we check that the actual number of arguments match the
// expectation.
// smarter.
Move(eax, Immediate(num_arguments));
mov(ebx, Immediate(ExternalReference(f, isolate())));
- CEntryStub ces(isolate(), 1);
+ CEntryStub ces(isolate(), 1, save_doubles);
CallStub(&ces);
}
// at the address pointed to by the addr register. Only works if addr is not
// in new space.
void RememberedSetHelper(Register object, // Used for debug code.
- Register addr,
- Register scratch,
+ Register addr, Register scratch,
+ SaveFPRegsMode save_fp,
RememberedSetFinalAction and_then);
void CheckPageFlag(Register object,
// The offset is the offset from the start of the object, not the offset from
// the tagged HeapObject pointer. For use with FieldOperand(reg, off).
void RecordWriteField(
- Register object,
- int offset,
- Register value,
- Register scratch,
+ Register object, int offset, Register value, Register scratch,
+ SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
// As above, but the offset has the tag presubtracted. For use with
// Operand(reg, off).
void RecordWriteContextSlot(
- Register context,
- int offset,
- Register value,
- Register scratch,
+ Register context, int offset, Register value, Register scratch,
+ SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
kPointersToHereMaybeInteresting) {
- RecordWriteField(context,
- offset + kHeapObjectTag,
- value,
- scratch,
- remembered_set_action,
- smi_check,
+ RecordWriteField(context, offset + kHeapObjectTag, value, scratch, save_fp,
+ remembered_set_action, smi_check,
pointers_to_here_check_for_value);
}
// filters out smis so it does not update the write barrier if the
// value is a smi.
void RecordWriteArray(
- Register array,
- Register value,
- Register index,
+ Register array, Register value, Register index, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
// operation. RecordWrite filters out smis so it does not update the
// write barrier if the value is a smi.
void RecordWrite(
- Register object,
- Register address,
- Register value,
+ Register object, Register address, Register value, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
// For page containing |object| mark the region covering the object's map
// dirty. |object| is the object being stored into, |map| is the Map object
// that was stored.
- void RecordWriteForMap(
- Register object,
- Handle<Map> map,
- Register scratch1,
- Register scratch2);
+ void RecordWriteForMap(Register object, Handle<Map> map, Register scratch1,
+ Register scratch2, SaveFPRegsMode save_fp);
// ---------------------------------------------------------------------------
// Debugger Support
// arguments in register eax and sets up the number of arguments in
// register edi and the pointer to the first argument in register
// esi.
- void EnterExitFrame();
+ void EnterExitFrame(bool save_doubles);
void EnterApiExitFrame(int argc);
// Leave the current exit frame. Expects the return value in
// register eax:edx (untouched) and the pointer to the first
// argument in register esi.
- void LeaveExitFrame();
+ void LeaveExitFrame(bool save_doubles);
// Leave the current exit frame. Expects the return value in
// register eax (untouched).
// FCmp is similar to integer cmp, but requires unsigned
// jcc instructions (je, ja, jae, jb, jbe, je, and jz).
void FCmp();
+ void FXamMinusZero();
+ void FXamSign();
+ void X87CheckIA();
+ void X87SetRC(int rc);
void ClampUint8(Register reg);
+ void ClampTOSToUint8(Register result_reg);
void SlowTruncateToI(Register result_reg, Register input_reg,
int offset = HeapNumber::kValueOffset - kHeapObjectTag);
void StubReturn(int argc);
// Call a runtime routine.
- void CallRuntime(const Runtime::Function* f, int num_arguments);
- // Convenience function: Same as above, but takes the fid instead.
- void CallRuntime(Runtime::FunctionId id) {
+ void CallRuntime(const Runtime::Function* f, int num_arguments,
+ SaveFPRegsMode save_doubles = kDontSaveFPRegs);
+ void CallRuntimeSaveDoubles(Runtime::FunctionId id) {
const Runtime::Function* function = Runtime::FunctionForId(id);
- CallRuntime(function, function->nargs);
+ CallRuntime(function, function->nargs, kSaveFPRegs);
}
- void CallRuntime(Runtime::FunctionId id, int num_arguments) {
- CallRuntime(Runtime::FunctionForId(id), num_arguments);
+
+ // Convenience function: Same as above, but takes the fid instead.
+ void CallRuntime(Runtime::FunctionId id, int num_arguments,
+ SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
+ CallRuntime(Runtime::FunctionForId(id), num_arguments, save_doubles);
}
// Convenience function: call an external reference.
const CallWrapper& call_wrapper = NullCallWrapper());
void EnterExitFramePrologue();
- void EnterExitFrameEpilogue(int argc);
+ void EnterExitFrameEpilogue(int argc, bool save_doubles);
void LeaveExitFrameEpilogue(bool restore_context);
__ fprem1();
__ fincstp();
__ ftst();
+ __ fxam();
__ fxch(3);
__ fld_s(Operand(ebx, ecx, times_4, 10000));
__ fstp_s(Operand(ebx, ecx, times_4, 10000));
__ fninit();
__ nop();
+ __ fldcw(Operand(ebx, ecx, times_4, 10000));
+ __ fnstcw(Operand(ebx, ecx, times_4, 10000));
+ __ fadd_d(Operand(ebx, ecx, times_4, 10000));
+ __ fnsave(Operand(ebx, ecx, times_4, 10000));
+ __ frstor(Operand(ebx, ecx, times_4, 10000));
+
// xchg.
{
__ xchg(eax, eax);