diff --git a/src/ic/x87/handler-compiler-x87.cc b/src/ic/x87/handler-compiler-x87.cc index 8370f68cbb..e706998c38 100644 --- a/src/ic/x87/handler-compiler-x87.cc +++ b/src/ic/x87/handler-compiler-x87.cc @@ -411,7 +411,7 @@ void NamedStoreHandlerCompiler::GenerateStoreTransition( // 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)); @@ -445,7 +445,7 @@ void NamedStoreHandlerCompiler::GenerateStoreTransition( __ 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. @@ -464,7 +464,7 @@ void NamedStoreHandlerCompiler::GenerateStoreTransition( __ mov(storage_reg, value_reg); } __ RecordWriteField(scratch1, offset, storage_reg, receiver_reg, - EMIT_REMEMBERED_SET, smi_check); + kDontSaveFPRegs, EMIT_REMEMBERED_SET, smi_check); } } diff --git a/src/ic/x87/ic-x87.cc b/src/ic/x87/ic-x87.cc index 2cd6ea11ff..499d8da2ef 100644 --- a/src/ic/x87/ic-x87.cc +++ b/src/ic/x87/ic-x87.cc @@ -133,7 +133,7 @@ static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss_label, // Update write barrier. Make sure not to clobber the value. __ mov(r1, value); - __ RecordWrite(elements, r0, r1); + __ RecordWrite(elements, r0, r1, kDontSaveFPRegs); } @@ -546,7 +546,7 @@ void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) { __ 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. @@ -555,7 +555,7 @@ void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) { __ 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); @@ -624,7 +624,8 @@ static void KeyedStoreGenerateGenericHelper( __ 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); diff --git a/src/x87/assembler-x87-inl.h b/src/x87/assembler-x87-inl.h index 25ecfcf137..6555ccdd83 100644 --- a/src/x87/assembler-x87-inl.h +++ b/src/x87/assembler-x87-inl.h @@ -45,7 +45,7 @@ namespace v8 { namespace internal { -bool CpuFeatures::SupportsCrankshaft() { return false; } +bool CpuFeatures::SupportsCrankshaft() { return true; } static const byte kCallOpcode = 0xE8; diff --git a/src/x87/assembler-x87.cc b/src/x87/assembler-x87.cc index 8f92249158..9e1c8836fc 100644 --- a/src/x87/assembler-x87.cc +++ b/src/x87/assembler-x87.cc @@ -1519,6 +1519,20 @@ void Assembler::fst_s(const Operand& adr) { } +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); @@ -1598,6 +1612,13 @@ void Assembler::fchs() { } +void Assembler::fsqrt() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFA); +} + + void Assembler::fcos() { EnsureSpace ensure_space(this); EMIT(0xD9); @@ -1659,6 +1680,13 @@ void Assembler::fadd_i(int i) { } +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); @@ -1772,6 +1800,13 @@ void Assembler::ftst() { } +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); @@ -1833,6 +1868,20 @@ void Assembler::fnclex() { } +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); diff --git a/src/x87/assembler-x87.h b/src/x87/assembler-x87.h index a2bedcc3cc..d37c9d77dc 100644 --- a/src/x87/assembler-x87.h +++ b/src/x87/assembler-x87.h @@ -142,7 +142,7 @@ inline Register Register::FromAllocationIndex(int index) { struct X87Register { - static const int kMaxNumAllocatableRegisters = 8; + static const int kMaxNumAllocatableRegisters = 6; static const int kMaxNumRegisters = 8; static int NumAllocatableRegisters() { return kMaxNumAllocatableRegisters; @@ -852,6 +852,7 @@ class Assembler : public AssemblerBase { void fabs(); void fchs(); + void fsqrt(); void fcos(); void fsin(); void fptan(); @@ -862,6 +863,7 @@ class Assembler : public AssemblerBase { 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); @@ -884,14 +886,19 @@ class Assembler : public AssemblerBase { 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(); diff --git a/src/x87/builtins-x87.cc b/src/x87/builtins-x87.cc index 6857cdc269..d6311752c7 100644 --- a/src/x87/builtins-x87.cc +++ b/src/x87/builtins-x87.cc @@ -660,7 +660,8 @@ void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { } -static void Generate_NotifyStubFailureHelper(MacroAssembler* masm) { +static void Generate_NotifyStubFailureHelper(MacroAssembler* masm, + SaveFPRegsMode save_doubles) { // Enter an internal frame. { FrameScope scope(masm, StackFrame::INTERNAL); @@ -669,7 +670,7 @@ static void Generate_NotifyStubFailureHelper(MacroAssembler* masm) { // 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. } @@ -680,13 +681,12 @@ static void Generate_NotifyStubFailureHelper(MacroAssembler* masm) { 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); } diff --git a/src/x87/code-stubs-x87.cc b/src/x87/code-stubs-x87.cc index 35514c3560..215d1a0fa4 100644 --- a/src/x87/code-stubs-x87.cc +++ b/src/x87/code-stubs-x87.cc @@ -127,6 +127,11 @@ void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { // 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); @@ -136,6 +141,11 @@ void StoreBufferOverflowStub::Generate(MacroAssembler* 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); } @@ -1115,16 +1125,12 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ 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 = @@ -1618,7 +1624,8 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) { __ 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); @@ -1989,12 +1996,19 @@ void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { 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(); } @@ -2010,7 +2024,7 @@ void CEntryStub::Generate(MacroAssembler* masm) { 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) @@ -2066,7 +2080,7 @@ void CEntryStub::Generate(MacroAssembler* masm) { } // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(); + __ LeaveExitFrame(save_doubles()); __ ret(0); // Handling of exception. @@ -3545,6 +3559,8 @@ void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime( Isolate* isolate) { StoreBufferOverflowStub stub(isolate, kDontSaveFPRegs); stub.GetCode(); + StoreBufferOverflowStub stub2(isolate, kSaveFPRegs); + stub2.GetCode(); } @@ -3564,7 +3580,7 @@ void RecordWriteStub::Generate(MacroAssembler* masm) { __ 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); @@ -3608,7 +3624,7 @@ void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { 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); @@ -3625,7 +3641,7 @@ void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { 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()); @@ -3638,7 +3654,7 @@ void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm) { ExternalReference::incremental_marking_record_write_function(isolate()), argument_count); - regs_.RestoreCallerSaveRegisters(masm); + regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode()); } @@ -3669,7 +3685,7 @@ void RecordWriteStub::CheckNeedsToInformIncrementalMarker( 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); @@ -3714,7 +3730,7 @@ void RecordWriteStub::CheckNeedsToInformIncrementalMarker( 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); @@ -3784,8 +3800,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { 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); @@ -3814,7 +3829,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { 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; diff --git a/src/x87/code-stubs-x87.h b/src/x87/code-stubs-x87.h index 49462bc5c4..03ff477f6a 100644 --- a/src/x87/code-stubs-x87.h +++ b/src/x87/code-stubs-x87.h @@ -116,11 +116,9 @@ class NameDictionaryLookupStub: public PlatformCodeStub { 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. @@ -128,7 +126,8 @@ class RecordWriteStub: public PlatformCodeStub { 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) @@ -271,12 +270,23 @@ class RecordWriteStub: public PlatformCodeStub { // 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); } @@ -348,10 +358,15 @@ class RecordWriteStub: public PlatformCodeStub { return RememberedSetActionBits::decode(minor_key_); } + SaveFPRegsMode save_fp_regs_mode() const { + return SaveFPRegsModeBits::decode(minor_key_); + } + class ObjectBits: public BitField {}; class ValueBits: public BitField {}; class AddressBits: public BitField {}; class RememberedSetActionBits: public BitField {}; + class SaveFPRegsModeBits : public BitField {}; RegisterAllocation regs_; diff --git a/src/x87/codegen-x87.cc b/src/x87/codegen-x87.cc index 56d273cf0a..e33959e65b 100644 --- a/src/x87/codegen-x87.cc +++ b/src/x87/codegen-x87.cc @@ -217,12 +217,8 @@ void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( // 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); } @@ -275,12 +271,8 @@ void ElementsTransitionGenerator::GenerateSmiToDouble( // 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)); @@ -339,12 +331,8 @@ void ElementsTransitionGenerator::GenerateSmiToDouble( // 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); } @@ -399,12 +387,8 @@ void ElementsTransitionGenerator::GenerateDoubleToObject( // 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. @@ -433,10 +417,7 @@ void ElementsTransitionGenerator::GenerateDoubleToObject( __ 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); @@ -455,20 +436,12 @@ void ElementsTransitionGenerator::GenerateDoubleToObject( // 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); diff --git a/src/x87/deoptimizer-x87.cc b/src/x87/deoptimizer-x87.cc index e873ac573d..a76c7a709d 100644 --- a/src/x87/deoptimizer-x87.cc +++ b/src/x87/deoptimizer-x87.cc @@ -204,8 +204,10 @@ void Deoptimizer::SetPlatformCompiledStubRegisters( 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); + } } @@ -230,9 +232,42 @@ void Deoptimizer::EntryGenerator::Generate() { // 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)); @@ -245,6 +280,7 @@ void Deoptimizer::EntryGenerator::Generate() { __ sub(edx, ebp); __ neg(edx); + __ push(edi); // Allocate a new deoptimizer object. __ PrepareCallCFunction(6, eax); __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); @@ -260,6 +296,8 @@ void Deoptimizer::EntryGenerator::Generate() { __ 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())); @@ -270,13 +308,22 @@ void Deoptimizer::EntryGenerator::Generate() { __ 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. @@ -298,6 +345,7 @@ void Deoptimizer::EntryGenerator::Generate() { __ 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); @@ -307,6 +355,7 @@ void Deoptimizer::EntryGenerator::Generate() { ExternalReference::compute_output_frames_function(isolate()), 1); } __ pop(eax); + __ pop(edi); // If frame was dynamically aligned, pop padding. Label no_padding; @@ -345,6 +394,25 @@ void Deoptimizer::EntryGenerator::Generate() { __ 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())); diff --git a/src/x87/disasm-x87.cc b/src/x87/disasm-x87.cc index 53a8c29067..908e8b0439 100644 --- a/src/x87/disasm-x87.cc +++ b/src/x87/disasm-x87.cc @@ -702,7 +702,12 @@ int DisassemblerX87::MemoryFPUInstruction(int escape_opcode, 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; @@ -716,11 +721,27 @@ int DisassemblerX87::MemoryFPUInstruction(int escape_opcode, } 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; diff --git a/src/x87/full-codegen-x87.cc b/src/x87/full-codegen-x87.cc index 94ccbcfb55..58328e06fb 100644 --- a/src/x87/full-codegen-x87.cc +++ b/src/x87/full-codegen-x87.cc @@ -221,10 +221,8 @@ void FullCodeGenerator::Generate() { __ 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); @@ -708,7 +706,7 @@ void FullCodeGenerator::SetVar(Variable* var, 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); } } @@ -838,12 +836,9 @@ void FullCodeGenerator::VisitFunctionDeclaration( 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; } @@ -877,11 +872,8 @@ void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) { // 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); @@ -1783,9 +1775,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { // 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))); @@ -1942,7 +1933,8 @@ void FullCodeGenerator::VisitYield(Yield* expr) { 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); @@ -2016,7 +2008,8 @@ void FullCodeGenerator::VisitYield(Yield* expr) { 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)); @@ -2224,8 +2217,8 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) { // 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); } @@ -2433,7 +2426,7 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( if (var->IsContextSlot()) { __ mov(edx, eax); int offset = Context::SlotOffset(var->index()); - __ RecordWriteContextSlot(ecx, offset, edx, ebx); + __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs); } } @@ -3532,7 +3525,7 @@ void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { // 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); diff --git a/src/x87/lithium-codegen-x87.cc b/src/x87/lithium-codegen-x87.cc index ded2cd94ae..7697a1c753 100644 --- a/src/x87/lithium-codegen-x87.cc +++ b/src/x87/lithium-codegen-x87.cc @@ -254,10 +254,8 @@ bool LCodeGen::GeneratePrologue() { __ 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); @@ -269,6 +267,8 @@ bool LCodeGen::GeneratePrologue() { 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 @@ -327,6 +327,9 @@ void LCodeGen::GenerateOsrPrologue() { int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots(); DCHECK(slots >= 1); __ sub(esp, Immediate((slots - 1) * kPointerSize)); + + // Initailize FPU state. + __ fninit(); } @@ -342,8 +345,21 @@ void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) { 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())) { @@ -494,10 +510,27 @@ void LCodeGen::X87LoadForUsage(X87Register reg) { 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; } @@ -572,6 +605,22 @@ void LCodeGen::X87Mov(X87Register dst, Operand src, X87OperandType opts) { } +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) { @@ -597,6 +646,9 @@ void LCodeGen::X87Mov(Operand dst, X87Register src, X87OperandType opts) { case kX87DoubleOperand: __ fst_d(dst); break; + case kX87FloatOperand: + __ fst_s(dst); + break; case kX87IntOperand: __ fist_s(dst); break; @@ -660,15 +712,39 @@ void LCodeGen::X87Stack::FlushIfNecessary(LInstruction* instr, LCodeGen* cgen) { } -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; } } @@ -678,13 +754,14 @@ void LCodeGen::EmitFlushX87ForDeopt() { // 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. } @@ -891,6 +968,9 @@ void LCodeGen::AddToTranslation(LEnvironment* environment, } 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())); @@ -925,13 +1005,12 @@ void LCodeGen::CallCode(Handle code, } -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); @@ -961,7 +1040,7 @@ void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, LOperand* context) { LoadContextFromDeferred(context); - __ CallRuntime(id); + __ CallRuntimeSaveDoubles(id); RecordSafepointWithRegisters( instr->pointer_map(), argc, Safepoint::kNoLazyDeopt); @@ -1035,6 +1114,12 @@ void LCodeGen::DeoptimizeIf(Condition cc, LInstruction* instr, __ 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); @@ -1042,14 +1127,18 @@ void LCodeGen::DeoptimizeIf(Condition cc, LInstruction* instr, __ 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); } @@ -1236,6 +1325,16 @@ void LCodeGen::DoLabel(LLabel* label) { 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); } @@ -1737,7 +1836,7 @@ void LCodeGen::DoMulI(LMulI* instr) { // 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); @@ -2118,8 +2217,58 @@ void LCodeGen::DoMathMinMax(LMathMinMax* 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); } } @@ -2164,6 +2313,13 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) { 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)); } @@ -2217,7 +2373,11 @@ void LCodeGen::DoBranch(LBranch* instr) { __ 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()); @@ -2473,7 +2633,10 @@ void LCodeGen::DoCompareMinusZeroAndBranch(LCompareMinusZeroAndBranch* instr) { 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 = masm()->isolate()->factory()->heap_number_map(); @@ -3058,12 +3221,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { ? 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); @@ -3732,7 +3891,9 @@ void LCodeGen::DoMathAbs(LMathAbs* instr) { 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. @@ -3748,47 +3909,350 @@ void LCodeGen::DoMathAbs(LMathAbs* instr) { 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); } @@ -3885,7 +4349,7 @@ void LCodeGen::DoCallNewArray(LCallNewArray* instr) { 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()); } @@ -3956,7 +4420,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { __ 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); } } @@ -3991,10 +4455,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { 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()); @@ -4054,8 +4515,7 @@ void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) { 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())); @@ -4191,10 +4651,7 @@ void LCodeGen::DoStoreKeyedFixedArray(LStoreKeyed* instr) { ? 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()); } @@ -4257,7 +4714,7 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { // 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)); @@ -4527,7 +4984,7 @@ void LCodeGen::DoDeferredNumberTagIU(LInstruction* instr, // 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); @@ -4557,7 +5014,9 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { // 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_); @@ -4568,7 +5027,7 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { __ jmp(deferred->entry()); } __ bind(deferred->exit()); - __ fstp_d(FieldOperand(reg, HeapNumber::kValueOffset)); + __ fst_d(FieldOperand(reg, HeapNumber::kValueOffset)); } @@ -4586,7 +5045,7 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { // 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); @@ -4635,7 +5094,7 @@ void LCodeGen::EmitNumberUntagDNoSSE2(LNumberUntagD* instr, Register input_reg, 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), @@ -4644,7 +5103,7 @@ void LCodeGen::EmitNumberUntagDNoSSE2(LNumberUntagD* instr, Register input_reg, 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()); @@ -4973,7 +5432,7 @@ void LCodeGen::DoDeferredInstanceMigration(LCheckMaps* instr, Register object) { PushSafepointRegistersScope scope(this); __ push(object); __ xor_(esi, esi); - __ CallRuntime(Runtime::kTryMigrateInstance); + __ CallRuntimeSaveDoubles(Runtime::kTryMigrateInstance); RecordSafepointWithRegisters( instr->pointer_map(), 1, Safepoint::kNoLazyDeopt); @@ -5043,7 +5502,10 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) { void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) { - UNREACHABLE(); + X87Register value_reg = ToX87Register(instr->unclamped()); + Register result_reg = ToRegister(instr->result()); + X87Fxch(value_reg); + __ ClampTOSToUint8(result_reg); } @@ -5177,12 +5639,32 @@ void LCodeGen::DoClampTToUint8NoSSE2(LClampTToUint8NoSSE2* instr) { 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); } @@ -5546,7 +6028,7 @@ void LCodeGen::DoDummyUse(LDummyUse* instr) { 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()); @@ -5693,7 +6175,7 @@ void LCodeGen::DoDeferredLoadMutableDouble(LLoadFieldByIndex* instr, __ push(object); __ push(index); __ xor_(esi, esi); - __ CallRuntime(Runtime::kLoadMutableDouble); + __ CallRuntimeSaveDoubles(Runtime::kLoadMutableDouble); RecordSafepointWithRegisters( instr->pointer_map(), 2, Safepoint::kNoLazyDeopt); __ StoreToSafepointRegisterSlot(object, eax); diff --git a/src/x87/lithium-codegen-x87.h b/src/x87/lithium-codegen-x87.h index 080a468292..88d2810573 100644 --- a/src/x87/lithium-codegen-x87.h +++ b/src/x87/lithium-codegen-x87.h @@ -5,6 +5,7 @@ #ifndef V8_X87_LITHIUM_CODEGEN_X87_H_ #define V8_X87_LITHIUM_CODEGEN_X87_H_ +#include #include "src/x87/lithium-x87.h" #include "src/base/logging.h" @@ -84,6 +85,8 @@ class LCodeGen: public LCodeGenBase { 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); @@ -198,9 +201,8 @@ class LCodeGen: public LCodeGenBase { 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, @@ -376,7 +378,7 @@ class LCodeGen: public LCodeGenBase { 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) { } @@ -393,14 +395,23 @@ class LCodeGen: public LCodeGenBase { } 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_--; @@ -425,6 +436,9 @@ class LCodeGen: public LCodeGenBase { MacroAssembler* masm_; }; X87Stack x87_stack_; + // block_id -> X87Stack*; + typedef std::map 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. @@ -458,6 +472,7 @@ class LCodeGen: public LCodeGenBase { friend class LDeferredCode; friend class LEnvironment; friend class SafepointGenerator; + friend class X87Stack; DISALLOW_COPY_AND_ASSIGN(LCodeGen); }; diff --git a/src/x87/lithium-gap-resolver-x87.cc b/src/x87/lithium-gap-resolver-x87.cc index b94e34f2c0..6a6427550c 100644 --- a/src/x87/lithium-gap-resolver-x87.cc +++ b/src/x87/lithium-gap-resolver-x87.cc @@ -317,10 +317,15 @@ void LGapResolver::EmitMove(int index) { } 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 diff --git a/src/x87/lithium-x87.cc b/src/x87/lithium-x87.cc index 02037c3b08..993f5adf27 100644 --- a/src/x87/lithium-x87.cc +++ b/src/x87/lithium-x87.cc @@ -484,6 +484,12 @@ LUnallocated* LChunkBuilder::ToUnallocated(Register reg) { } +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)); } @@ -616,6 +622,12 @@ LInstruction* LChunkBuilder::DefineFixed(LTemplateResultInstruction<1>* instr, } +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; @@ -872,6 +884,14 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { 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); @@ -931,7 +951,8 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, 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 @@ -979,7 +1000,9 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { 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())) { @@ -1182,16 +1205,16 @@ LInstruction* LChunkBuilder::DoMathFloor(HUnaryMathOperation* instr) { 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); } @@ -1225,11 +1248,11 @@ LInstruction* LChunkBuilder::DoMathClz32(HUnaryMathOperation* instr) { 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); } @@ -1242,8 +1265,7 @@ LInstruction* LChunkBuilder::DoMathSqrt(HUnaryMathOperation* 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); } @@ -1615,6 +1637,8 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { 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())); @@ -1627,15 +1651,19 @@ LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) { 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); } @@ -1697,9 +1725,8 @@ LInstruction* LChunkBuilder::DoCompareHoleAndBranch( 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); } @@ -2022,8 +2049,8 @@ LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) { 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); diff --git a/src/x87/lithium-x87.h b/src/x87/lithium-x87.h index 233eaf23f6..e5cced2ba3 100644 --- a/src/x87/lithium-x87.h +++ b/src/x87/lithium-x87.h @@ -413,6 +413,7 @@ class LGoto FINAL : public LTemplateInstruction<0, 0, 0> { } bool jumps_to_join() const { return block_->predecessors()->length() > 1; } + HBasicBlock* block() const { return block_; } private: HBasicBlock* block_; @@ -984,15 +985,11 @@ class LMathSqrt FINAL : public LTemplateInstruction<1, 1, 0> { }; -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") }; @@ -1025,15 +1022,11 @@ class LCmpHoleAndBranch FINAL : public LControlInstruction<1, 0> { }; -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") @@ -1508,15 +1501,17 @@ class LAddI FINAL : public LTemplateInstruction<1, 2, 0> { }; -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) @@ -2037,11 +2032,12 @@ class LCallRuntime FINAL : public LTemplateInstruction<1, 1, 0> { 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(); } }; @@ -2881,6 +2877,8 @@ class LChunkBuilder FINAL : public LChunkBuilderBase { 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. diff --git a/src/x87/macro-assembler-x87.cc b/src/x87/macro-assembler-x87.cc index 66f5703343..6f83f49087 100644 --- a/src/x87/macro-assembler-x87.cc +++ b/src/x87/macro-assembler-x87.cc @@ -148,8 +148,7 @@ void MacroAssembler::InNewSpace( 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()) { @@ -180,7 +179,7 @@ void MacroAssembler::RememberedSetHelper( 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); @@ -191,6 +190,31 @@ void MacroAssembler::RememberedSetHelper( } +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)); @@ -270,11 +294,8 @@ void MacroAssembler::LoadUint32NoSSE2(Register src) { 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. @@ -294,8 +315,8 @@ void MacroAssembler::RecordWriteArray( 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); @@ -309,13 +330,9 @@ void MacroAssembler::RecordWriteArray( 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; @@ -338,8 +355,8 @@ void MacroAssembler::RecordWriteField( 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); @@ -352,11 +369,9 @@ void MacroAssembler::RecordWriteField( } -void MacroAssembler::RecordWriteForMap( - Register object, - Handle map, - Register scratch1, - Register scratch2) { +void MacroAssembler::RecordWriteForMap(Register object, Handle map, + Register scratch1, Register scratch2, + SaveFPRegsMode save_fp) { Label done; Register address = scratch1; @@ -393,7 +408,8 @@ void MacroAssembler::RecordWriteForMap( &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); @@ -413,11 +429,8 @@ void MacroAssembler::RecordWriteForMap( 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)); @@ -461,8 +474,8 @@ void MacroAssembler::RecordWrite( &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); @@ -707,6 +720,53 @@ void MacroAssembler::FCmp() { } +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; @@ -844,8 +904,17 @@ void MacroAssembler::EnterExitFramePrologue() { } -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(); @@ -859,7 +928,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int argc) { } -void MacroAssembler::EnterExitFrame() { +void MacroAssembler::EnterExitFrame(bool save_doubles) { EnterExitFramePrologue(); // Set up argc and argv in callee-saved registers. @@ -868,17 +937,23 @@ void MacroAssembler::EnterExitFrame() { 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)); @@ -1908,8 +1983,8 @@ void MacroAssembler::IndexFromHash(Register hash, Register index) { } -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. @@ -1921,7 +1996,7 @@ void MacroAssembler::CallRuntime(const Runtime::Function* f, // smarter. Move(eax, Immediate(num_arguments)); mov(ebx, Immediate(ExternalReference(f, isolate()))); - CEntryStub ces(isolate(), 1); + CEntryStub ces(isolate(), 1, save_doubles); CallStub(&ces); } diff --git a/src/x87/macro-assembler-x87.h b/src/x87/macro-assembler-x87.h index 1fdca3c9f6..17cd9f49a9 100644 --- a/src/x87/macro-assembler-x87.h +++ b/src/x87/macro-assembler-x87.h @@ -74,8 +74,8 @@ class MacroAssembler: public Assembler { // 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, @@ -146,10 +146,8 @@ class MacroAssembler: public Assembler { // 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 = @@ -158,20 +156,14 @@ class MacroAssembler: public Assembler { // 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); } @@ -182,9 +174,7 @@ class MacroAssembler: public Assembler { // 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 = @@ -196,9 +186,7 @@ class MacroAssembler: public Assembler { // 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 = @@ -207,11 +195,8 @@ class MacroAssembler: public Assembler { // 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, - Register scratch1, - Register scratch2); + void RecordWriteForMap(Register object, Handle map, Register scratch1, + Register scratch2, SaveFPRegsMode save_fp); // --------------------------------------------------------------------------- // Debugger Support @@ -226,14 +211,14 @@ class MacroAssembler: public Assembler { // 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). @@ -435,8 +420,13 @@ class MacroAssembler: public Assembler { // 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); @@ -717,14 +707,17 @@ class MacroAssembler: public Assembler { 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. @@ -956,7 +949,7 @@ class MacroAssembler: public Assembler { const CallWrapper& call_wrapper = NullCallWrapper()); void EnterExitFramePrologue(); - void EnterExitFrameEpilogue(int argc); + void EnterExitFrameEpilogue(int argc, bool save_doubles); void LeaveExitFrameEpilogue(bool restore_context); diff --git a/test/cctest/test-disasm-x87.cc b/test/cctest/test-disasm-x87.cc index 17c49af6c3..6cd33e5574 100644 --- a/test/cctest/test-disasm-x87.cc +++ b/test/cctest/test-disasm-x87.cc @@ -349,6 +349,7 @@ TEST(DisasmIa320) { __ fprem1(); __ fincstp(); __ ftst(); + __ fxam(); __ fxch(3); __ fld_s(Operand(ebx, ecx, times_4, 10000)); __ fstp_s(Operand(ebx, ecx, times_4, 10000)); @@ -378,6 +379,12 @@ TEST(DisasmIa320) { __ 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);