| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef jit_x86_shared_Assembler_x86_shared_h |
| #define jit_x86_shared_Assembler_x86_shared_h |
| |
| #include <cstddef> |
| |
| #include "jit/shared/Assembler-shared.h" |
| |
| #if defined(JS_CODEGEN_X86) |
| # include "jit/x86/BaseAssembler-x86.h" |
| #elif defined(JS_CODEGEN_X64) |
| # include "jit/x64/BaseAssembler-x64.h" |
| #else |
| # error "Unknown architecture!" |
| #endif |
| |
| namespace js { |
| namespace jit { |
| |
| struct ScratchFloat32Scope : public AutoFloatRegisterScope |
| { |
| explicit ScratchFloat32Scope(MacroAssembler& masm) |
| : AutoFloatRegisterScope(masm, ScratchFloat32Reg) |
| { } |
| }; |
| |
| struct ScratchDoubleScope : public AutoFloatRegisterScope |
| { |
| explicit ScratchDoubleScope(MacroAssembler& masm) |
| : AutoFloatRegisterScope(masm, ScratchDoubleReg) |
| { } |
| }; |
| |
| struct ScratchSimd128Scope : public AutoFloatRegisterScope |
| { |
| explicit ScratchSimd128Scope(MacroAssembler& masm) |
| : AutoFloatRegisterScope(masm, ScratchSimd128Reg) |
| { } |
| }; |
| |
| class Operand |
| { |
| public: |
| enum Kind { |
| REG, |
| MEM_REG_DISP, |
| FPREG, |
| MEM_SCALE, |
| MEM_ADDRESS32 |
| }; |
| |
| private: |
| Kind kind_ : 4; |
| // Used as a Register::Encoding and a FloatRegister::Encoding. |
| uint32_t base_ : 5; |
| Scale scale_ : 3; |
| Register::Encoding index_ : 5; |
| int32_t disp_; |
| |
| public: |
| explicit Operand(Register reg) |
| : kind_(REG), |
| base_(reg.encoding()), |
| scale_(TimesOne), |
| index_(Registers::Invalid), |
| disp_(0) |
| { } |
| explicit Operand(FloatRegister reg) |
| : kind_(FPREG), |
| base_(reg.encoding()), |
| scale_(TimesOne), |
| index_(Registers::Invalid), |
| disp_(0) |
| { } |
| explicit Operand(const Address& address) |
| : kind_(MEM_REG_DISP), |
| base_(address.base.encoding()), |
| scale_(TimesOne), |
| index_(Registers::Invalid), |
| disp_(address.offset) |
| { } |
| explicit Operand(const BaseIndex& address) |
| : kind_(MEM_SCALE), |
| base_(address.base.encoding()), |
| scale_(address.scale), |
| index_(address.index.encoding()), |
| disp_(address.offset) |
| { } |
| Operand(Register base, Register index, Scale scale, int32_t disp = 0) |
| : kind_(MEM_SCALE), |
| base_(base.encoding()), |
| scale_(scale), |
| index_(index.encoding()), |
| disp_(disp) |
| { } |
| Operand(Register reg, int32_t disp) |
| : kind_(MEM_REG_DISP), |
| base_(reg.encoding()), |
| scale_(TimesOne), |
| index_(Registers::Invalid), |
| disp_(disp) |
| { } |
| explicit Operand(AbsoluteAddress address) |
| : kind_(MEM_ADDRESS32), |
| base_(Registers::Invalid), |
| scale_(TimesOne), |
| index_(Registers::Invalid), |
| disp_(X86Encoding::AddressImmediate(address.addr)) |
| { } |
| explicit Operand(PatchedAbsoluteAddress address) |
| : kind_(MEM_ADDRESS32), |
| base_(Registers::Invalid), |
| scale_(TimesOne), |
| index_(Registers::Invalid), |
| disp_(X86Encoding::AddressImmediate(address.addr)) |
| { } |
| |
| Address toAddress() const { |
| MOZ_ASSERT(kind() == MEM_REG_DISP); |
| return Address(Register::FromCode(base()), disp()); |
| } |
| |
| BaseIndex toBaseIndex() const { |
| MOZ_ASSERT(kind() == MEM_SCALE); |
| return BaseIndex(Register::FromCode(base()), Register::FromCode(index()), scale(), disp()); |
| } |
| |
| Kind kind() const { |
| return kind_; |
| } |
| Register::Encoding reg() const { |
| MOZ_ASSERT(kind() == REG); |
| return Register::Encoding(base_); |
| } |
| Register::Encoding base() const { |
| MOZ_ASSERT(kind() == MEM_REG_DISP || kind() == MEM_SCALE); |
| return Register::Encoding(base_); |
| } |
| Register::Encoding index() const { |
| MOZ_ASSERT(kind() == MEM_SCALE); |
| return index_; |
| } |
| Scale scale() const { |
| MOZ_ASSERT(kind() == MEM_SCALE); |
| return scale_; |
| } |
| FloatRegister::Encoding fpu() const { |
| MOZ_ASSERT(kind() == FPREG); |
| return FloatRegister::Encoding(base_); |
| } |
| int32_t disp() const { |
| MOZ_ASSERT(kind() == MEM_REG_DISP || kind() == MEM_SCALE); |
| return disp_; |
| } |
| void* address() const { |
| MOZ_ASSERT(kind() == MEM_ADDRESS32); |
| return reinterpret_cast<void*>(disp_); |
| } |
| |
| bool containsReg(Register r) const { |
| switch (kind()) { |
| case REG: return r.encoding() == reg(); |
| case MEM_REG_DISP: return r.encoding() == base(); |
| case MEM_SCALE: return r.encoding() == base() || r.encoding() == index(); |
| default: MOZ_CRASH("Unexpected Operand kind"); |
| } |
| return false; |
| } |
| }; |
| |
| inline Imm32 |
| Imm64::firstHalf() const |
| { |
| return low(); |
| } |
| |
| inline Imm32 |
| Imm64::secondHalf() const |
| { |
| return hi(); |
| } |
| |
| class CPUInfo |
| { |
| public: |
| // As the SSE's were introduced in order, the presence of a later SSE implies |
| // the presence of an earlier SSE. For example, SSE4_2 support implies SSE2 support. |
| enum SSEVersion { |
| UnknownSSE = 0, |
| NoSSE = 1, |
| SSE = 2, |
| SSE2 = 3, |
| SSE3 = 4, |
| SSSE3 = 5, |
| SSE4_1 = 6, |
| SSE4_2 = 7 |
| }; |
| |
| static SSEVersion GetSSEVersion() { |
| if (maxSSEVersion == UnknownSSE) |
| SetSSEVersion(); |
| |
| MOZ_ASSERT(maxSSEVersion != UnknownSSE); |
| MOZ_ASSERT_IF(maxEnabledSSEVersion != UnknownSSE, maxSSEVersion <= maxEnabledSSEVersion); |
| return maxSSEVersion; |
| } |
| |
| static bool IsAVXPresent() { |
| if (MOZ_UNLIKELY(maxSSEVersion == UnknownSSE)) |
| SetSSEVersion(); |
| |
| MOZ_ASSERT_IF(!avxEnabled, !avxPresent); |
| return avxPresent; |
| } |
| |
| private: |
| static SSEVersion maxSSEVersion; |
| static SSEVersion maxEnabledSSEVersion; |
| static bool avxPresent; |
| static bool avxEnabled; |
| |
| static void SetSSEVersion(); |
| |
| public: |
| static bool IsSSE2Present() { |
| #ifdef JS_CODEGEN_X64 |
| return true; |
| #else |
| return GetSSEVersion() >= SSE2; |
| #endif |
| } |
| static bool IsSSE3Present() { return GetSSEVersion() >= SSE3; } |
| static bool IsSSSE3Present() { return GetSSEVersion() >= SSSE3; } |
| static bool IsSSE41Present() { return GetSSEVersion() >= SSE4_1; } |
| static bool IsSSE42Present() { return GetSSEVersion() >= SSE4_2; } |
| |
| #ifdef JS_CODEGEN_X86 |
| static void SetFloatingPointDisabled() { maxEnabledSSEVersion = NoSSE; avxEnabled = false; } |
| #endif |
| static void SetSSE3Disabled() { maxEnabledSSEVersion = SSE2; avxEnabled = false; } |
| static void SetSSE4Disabled() { maxEnabledSSEVersion = SSSE3; avxEnabled = false; } |
| static void SetAVXEnabled() { avxEnabled = true; } |
| }; |
| |
| class AssemblerX86Shared : public AssemblerShared |
| { |
| protected: |
| struct RelativePatch { |
| int32_t offset; |
| void* target; |
| Relocation::Kind kind; |
| |
| RelativePatch(int32_t offset, void* target, Relocation::Kind kind) |
| : offset(offset), |
| target(target), |
| kind(kind) |
| { } |
| }; |
| |
| Vector<RelativePatch, 8, SystemAllocPolicy> jumps_; |
| CompactBufferWriter jumpRelocations_; |
| CompactBufferWriter dataRelocations_; |
| CompactBufferWriter preBarriers_; |
| |
| void writeDataRelocation(ImmGCPtr ptr) { |
| if (ptr.value) { |
| if (gc::IsInsideNursery(ptr.value)) |
| embedsNurseryPointers_ = true; |
| dataRelocations_.writeUnsigned(masm.currentOffset()); |
| } |
| } |
| void writePrebarrierOffset(CodeOffset label) { |
| preBarriers_.writeUnsigned(label.offset()); |
| } |
| |
| protected: |
| X86Encoding::BaseAssemblerSpecific masm; |
| |
| typedef X86Encoding::JmpSrc JmpSrc; |
| typedef X86Encoding::JmpDst JmpDst; |
| |
| public: |
| AssemblerX86Shared() |
| { |
| if (!HasAVX()) |
| masm.disableVEX(); |
| } |
| |
| enum Condition { |
| Equal = X86Encoding::ConditionE, |
| NotEqual = X86Encoding::ConditionNE, |
| Above = X86Encoding::ConditionA, |
| AboveOrEqual = X86Encoding::ConditionAE, |
| Below = X86Encoding::ConditionB, |
| BelowOrEqual = X86Encoding::ConditionBE, |
| GreaterThan = X86Encoding::ConditionG, |
| GreaterThanOrEqual = X86Encoding::ConditionGE, |
| LessThan = X86Encoding::ConditionL, |
| LessThanOrEqual = X86Encoding::ConditionLE, |
| Overflow = X86Encoding::ConditionO, |
| Signed = X86Encoding::ConditionS, |
| NotSigned = X86Encoding::ConditionNS, |
| Zero = X86Encoding::ConditionE, |
| NonZero = X86Encoding::ConditionNE, |
| Parity = X86Encoding::ConditionP, |
| NoParity = X86Encoding::ConditionNP |
| }; |
| |
| // If this bit is set, the vucomisd operands have to be inverted. |
| static const int DoubleConditionBitInvert = 0x10; |
| |
| // Bit set when a DoubleCondition does not map to a single x86 condition. |
| // The macro assembler has to special-case these conditions. |
| static const int DoubleConditionBitSpecial = 0x20; |
| static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; |
| |
| enum DoubleCondition { |
| // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. |
| DoubleOrdered = NoParity, |
| DoubleEqual = Equal | DoubleConditionBitSpecial, |
| DoubleNotEqual = NotEqual, |
| DoubleGreaterThan = Above, |
| DoubleGreaterThanOrEqual = AboveOrEqual, |
| DoubleLessThan = Above | DoubleConditionBitInvert, |
| DoubleLessThanOrEqual = AboveOrEqual | DoubleConditionBitInvert, |
| // If either operand is NaN, these conditions always evaluate to true. |
| DoubleUnordered = Parity, |
| DoubleEqualOrUnordered = Equal, |
| DoubleNotEqualOrUnordered = NotEqual | DoubleConditionBitSpecial, |
| DoubleGreaterThanOrUnordered = Below | DoubleConditionBitInvert, |
| DoubleGreaterThanOrEqualOrUnordered = BelowOrEqual | DoubleConditionBitInvert, |
| DoubleLessThanOrUnordered = Below, |
| DoubleLessThanOrEqualOrUnordered = BelowOrEqual |
| }; |
| |
| enum NaNCond { |
| NaN_HandledByCond, |
| NaN_IsTrue, |
| NaN_IsFalse |
| }; |
| |
| // If the primary condition returned by ConditionFromDoubleCondition doesn't |
| // handle NaNs properly, return NaN_IsFalse if the comparison should be |
| // overridden to return false on NaN, NaN_IsTrue if it should be overridden |
| // to return true on NaN, or NaN_HandledByCond if no secondary check is |
| // needed. |
| static inline NaNCond NaNCondFromDoubleCondition(DoubleCondition cond) { |
| switch (cond) { |
| case DoubleOrdered: |
| case DoubleNotEqual: |
| case DoubleGreaterThan: |
| case DoubleGreaterThanOrEqual: |
| case DoubleLessThan: |
| case DoubleLessThanOrEqual: |
| case DoubleUnordered: |
| case DoubleEqualOrUnordered: |
| case DoubleGreaterThanOrUnordered: |
| case DoubleGreaterThanOrEqualOrUnordered: |
| case DoubleLessThanOrUnordered: |
| case DoubleLessThanOrEqualOrUnordered: |
| return NaN_HandledByCond; |
| case DoubleEqual: |
| return NaN_IsFalse; |
| case DoubleNotEqualOrUnordered: |
| return NaN_IsTrue; |
| } |
| |
| MOZ_CRASH("Unknown double condition"); |
| } |
| |
| static void StaticAsserts() { |
| // DoubleConditionBits should not interfere with x86 condition codes. |
| JS_STATIC_ASSERT(!((Equal | NotEqual | Above | AboveOrEqual | Below | |
| BelowOrEqual | Parity | NoParity) & DoubleConditionBits)); |
| } |
| |
| static Condition InvertCondition(Condition cond); |
| |
| // Return the primary condition to test. Some primary conditions may not |
| // handle NaNs properly and may therefore require a secondary condition. |
| // Use NaNCondFromDoubleCondition to determine what else is needed. |
| static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) { |
| return static_cast<Condition>(cond & ~DoubleConditionBits); |
| } |
| |
| static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); |
| |
| // MacroAssemblers hold onto gcthings, so they are traced by the GC. |
| void trace(JSTracer* trc); |
| |
| bool oom() const { |
| return AssemblerShared::oom() || |
| masm.oom() || |
| jumpRelocations_.oom() || |
| dataRelocations_.oom() || |
| preBarriers_.oom(); |
| } |
| |
| void setPrinter(Sprinter* sp) { |
| masm.setPrinter(sp); |
| } |
| |
| static const Register getStackPointer() { |
| return StackPointer; |
| } |
| |
| void executableCopy(void* buffer); |
| bool asmMergeWith(const AssemblerX86Shared& other) { |
| MOZ_ASSERT(other.jumps_.length() == 0); |
| if (!AssemblerShared::asmMergeWith(masm.size(), other)) |
| return false; |
| return masm.appendBuffer(other.masm); |
| } |
| void processCodeLabels(uint8_t* rawCode); |
| void copyJumpRelocationTable(uint8_t* dest); |
| void copyDataRelocationTable(uint8_t* dest); |
| void copyPreBarrierTable(uint8_t* dest); |
| |
| // Size of the instruction stream, in bytes. |
| size_t size() const { |
| return masm.size(); |
| } |
| // Size of the jump relocation table, in bytes. |
| size_t jumpRelocationTableBytes() const { |
| return jumpRelocations_.length(); |
| } |
| size_t dataRelocationTableBytes() const { |
| return dataRelocations_.length(); |
| } |
| size_t preBarrierTableBytes() const { |
| return preBarriers_.length(); |
| } |
| // Size of the data table, in bytes. |
| size_t bytesNeeded() const { |
| return size() + |
| jumpRelocationTableBytes() + |
| dataRelocationTableBytes() + |
| preBarrierTableBytes(); |
| } |
| |
| public: |
| void haltingAlign(int alignment) { |
| masm.haltingAlign(alignment); |
| } |
| void nopAlign(int alignment) { |
| masm.nopAlign(alignment); |
| } |
| void writeCodePointer(CodeOffset* label) { |
| // A CodeOffset only has one use, bake in the "end of list" value. |
| masm.jumpTablePointer(LabelBase::INVALID_OFFSET); |
| label->bind(masm.size()); |
| } |
| void movl(Imm32 imm32, Register dest) { |
| masm.movl_i32r(imm32.value, dest.encoding()); |
| } |
| void movl(Register src, Register dest) { |
| masm.movl_rr(src.encoding(), dest.encoding()); |
| } |
| void movl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.movl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.movl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.movl_mr(src.address(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movl(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.movl_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.movl_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movl_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.movl_rm(src.encoding(), dest.address()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movl(Imm32 imm32, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.movl_i32r(imm32.value, dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.movl_i32m(imm32.value, dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movl_i32m(imm32.value, dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| void xchgl(Register src, Register dest) { |
| masm.xchgl_rr(src.encoding(), dest.encoding()); |
| } |
| |
| // Eventually vmovapd should be overloaded to support loads and |
| // stores too. |
| void vmovapd(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovapd_rr(src.encoding(), dest.encoding()); |
| } |
| |
| void vmovaps(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovaps_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovaps(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovaps_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovaps_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| case Operand::FPREG: |
| masm.vmovaps_rr(src.fpu(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovaps(FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovaps_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovaps_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovups(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovups_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovups_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovups(FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovups_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovups_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| // vmovsd is only provided in load/store form since the |
| // register-to-register form has different semantics (it doesn't clobber |
| // the whole output register) and isn't needed currently. |
| void vmovsd(const Address& src, FloatRegister dest) { |
| masm.vmovsd_mr(src.offset, src.base.encoding(), dest.encoding()); |
| } |
| void vmovsd(const BaseIndex& src, FloatRegister dest) { |
| masm.vmovsd_mr(src.offset, src.base.encoding(), src.index.encoding(), src.scale, dest.encoding()); |
| } |
| void vmovsd(FloatRegister src, const Address& dest) { |
| masm.vmovsd_rm(src.encoding(), dest.offset, dest.base.encoding()); |
| } |
| void vmovsd(FloatRegister src, const BaseIndex& dest) { |
| masm.vmovsd_rm(src.encoding(), dest.offset, dest.base.encoding(), dest.index.encoding(), dest.scale); |
| } |
| // Although vmovss is not only provided in load/store form (for the same |
| // reasons as vmovsd above), the register to register form should be only |
| // used in contexts where we care about not clearing the higher lanes of |
| // the FloatRegister. |
| void vmovss(const Address& src, FloatRegister dest) { |
| masm.vmovss_mr(src.offset, src.base.encoding(), dest.encoding()); |
| } |
| void vmovss(const BaseIndex& src, FloatRegister dest) { |
| masm.vmovss_mr(src.offset, src.base.encoding(), src.index.encoding(), src.scale, dest.encoding()); |
| } |
| void vmovss(FloatRegister src, const Address& dest) { |
| masm.vmovss_rm(src.encoding(), dest.offset, dest.base.encoding()); |
| } |
| void vmovss(FloatRegister src, const BaseIndex& dest) { |
| masm.vmovss_rm(src.encoding(), dest.offset, dest.base.encoding(), dest.index.encoding(), dest.scale); |
| } |
| void vmovss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| masm.vmovss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vmovdqu(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovdqu_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovdqu_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovdqu(FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovdqu_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovdqu_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovdqa(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::FPREG: |
| masm.vmovdqa_rr(src.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmovdqa_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovdqa_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovdqa(FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovdqa_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovdqa_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovdqa(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovdqa_rr(src.encoding(), dest.encoding()); |
| } |
| void vcvtss2sd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvtss2sd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vcvtsd2ss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvtsd2ss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void movzbl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movzbl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movzbl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movsbl(Register src, Register dest) { |
| masm.movsbl_rr(src.encoding(), dest.encoding()); |
| } |
| void movsbl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movsbl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movsbl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movb(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movb_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movb_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movb(Imm32 src, Register dest) { |
| masm.movb_ir(src.value & 255, dest.encoding()); |
| } |
| void movb(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movb_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movb_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movb(Imm32 src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movb_im(src.value, dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movb_im(src.value, dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movzwl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.movzwl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.movzwl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movzwl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movzwl(Register src, Register dest) { |
| masm.movzwl_rr(src.encoding(), dest.encoding()); |
| } |
| void movw(const Operand& src, Register dest) { |
| masm.prefix_16_for_32(); |
| movl(src, dest); |
| } |
| void movw(Imm32 src, Register dest) { |
| masm.prefix_16_for_32(); |
| movl(src, dest); |
| } |
| void movw(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movw_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movw_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movw(Imm32 src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movw_im(src.value, dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movw_im(src.value, dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void movswl(Register src, Register dest) { |
| masm.movswl_rr(src.encoding(), dest.encoding()); |
| } |
| void movswl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.movswl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.movswl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void leal(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.leal_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.leal_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| protected: |
| void jSrc(Condition cond, Label* label) { |
| if (label->bound()) { |
| // The jump can be immediately encoded to the correct destination. |
| masm.jCC_i(static_cast<X86Encoding::Condition>(cond), JmpDst(label->offset())); |
| } else { |
| // Thread the jump list through the unpatched jump targets. |
| JmpSrc j = masm.jCC(static_cast<X86Encoding::Condition>(cond)); |
| JmpSrc prev = JmpSrc(label->use(j.offset())); |
| masm.setNextJump(j, prev); |
| } |
| } |
| void jmpSrc(Label* label) { |
| if (label->bound()) { |
| // The jump can be immediately encoded to the correct destination. |
| masm.jmp_i(JmpDst(label->offset())); |
| } else { |
| // Thread the jump list through the unpatched jump targets. |
| JmpSrc j = masm.jmp(); |
| JmpSrc prev = JmpSrc(label->use(j.offset())); |
| masm.setNextJump(j, prev); |
| } |
| } |
| |
| // Comparison of EAX against the address given by a Label. |
| JmpSrc cmpSrc(Label* label) { |
| JmpSrc j = masm.cmp_eax(); |
| if (label->bound()) { |
| // The jump can be immediately patched to the correct destination. |
| masm.linkJump(j, JmpDst(label->offset())); |
| } else { |
| // Thread the jump list through the unpatched jump targets. |
| JmpSrc prev = JmpSrc(label->use(j.offset())); |
| masm.setNextJump(j, prev); |
| } |
| return j; |
| } |
| |
| JmpSrc jSrc(Condition cond, RepatchLabel* label) { |
| JmpSrc j = masm.jCC(static_cast<X86Encoding::Condition>(cond)); |
| if (label->bound()) { |
| // The jump can be immediately patched to the correct destination. |
| masm.linkJump(j, JmpDst(label->offset())); |
| } else { |
| label->use(j.offset()); |
| } |
| return j; |
| } |
| JmpSrc jmpSrc(RepatchLabel* label) { |
| JmpSrc j = masm.jmp(); |
| if (label->bound()) { |
| // The jump can be immediately patched to the correct destination. |
| masm.linkJump(j, JmpDst(label->offset())); |
| } else { |
| // Thread the jump list through the unpatched jump targets. |
| label->use(j.offset()); |
| } |
| return j; |
| } |
| |
| public: |
| void nop() { masm.nop(); } |
| void twoByteNop() { masm.twoByteNop(); } |
| void j(Condition cond, Label* label) { jSrc(cond, label); } |
| void jmp(Label* label) { jmpSrc(label); } |
| void j(Condition cond, RepatchLabel* label) { jSrc(cond, label); } |
| void jmp(RepatchLabel* label) { jmpSrc(label); } |
| |
| void jmp(const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.jmp_m(op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.jmp_m(op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| case Operand::REG: |
| masm.jmp_r(op.reg()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void cmpEAX(Label* label) { cmpSrc(label); } |
| void bind(Label* label) { |
| JmpDst dst(masm.label()); |
| if (label->used()) { |
| bool more; |
| JmpSrc jmp(label->offset()); |
| do { |
| JmpSrc next; |
| more = masm.nextJump(jmp, &next); |
| masm.linkJump(jmp, dst); |
| jmp = next; |
| } while (more); |
| } |
| label->bind(dst.offset()); |
| } |
| void bind(RepatchLabel* label) { |
| JmpDst dst(masm.label()); |
| if (label->used()) { |
| JmpSrc jmp(label->offset()); |
| masm.linkJump(jmp, dst); |
| } |
| label->bind(dst.offset()); |
| } |
| void use(CodeOffset* label) { |
| label->bind(currentOffset()); |
| } |
| uint32_t currentOffset() { |
| return masm.label().offset(); |
| } |
| |
| // Re-routes pending jumps to a new label. |
| void retargetWithOffset(size_t baseOffset, const LabelBase* label, LabelBase* target) { |
| if (!label->used()) |
| return; |
| bool more; |
| JmpSrc jmp(label->offset() + baseOffset); |
| do { |
| JmpSrc next; |
| more = masm.nextJump(jmp, &next); |
| if (target->bound()) { |
| // The jump can be immediately patched to the correct destination. |
| masm.linkJump(jmp, JmpDst(target->offset())); |
| } else { |
| // Thread the jump list through the unpatched jump targets. |
| JmpSrc prev(target->use(jmp.offset())); |
| masm.setNextJump(jmp, prev); |
| } |
| jmp = JmpSrc(next.offset() + baseOffset); |
| } while (more); |
| } |
| void retarget(Label* label, Label* target) { |
| retargetWithOffset(0, label, target); |
| label->reset(); |
| } |
| |
| static void Bind(uint8_t* raw, CodeOffset* label, const void* address) { |
| if (label->bound()) { |
| intptr_t offset = label->offset(); |
| X86Encoding::SetPointer(raw + offset, address); |
| } |
| } |
| |
| // See Bind and X86Encoding::setPointer. |
| size_t labelToPatchOffset(CodeOffset label) { |
| return label.offset() - sizeof(void*); |
| } |
| |
| void ret() { |
| masm.ret(); |
| } |
| void retn(Imm32 n) { |
| // Remove the size of the return address which is included in the frame. |
| masm.ret_i(n.value - sizeof(void*)); |
| } |
| CodeOffset call(Label* label) { |
| if (label->bound()) { |
| masm.linkJump(masm.call(), JmpDst(label->offset())); |
| } else { |
| JmpSrc j = masm.call(); |
| JmpSrc prev = JmpSrc(label->use(j.offset())); |
| masm.setNextJump(j, prev); |
| } |
| return CodeOffset(masm.currentOffset()); |
| } |
| CodeOffset call(Register reg) { |
| masm.call_r(reg.encoding()); |
| return CodeOffset(masm.currentOffset()); |
| } |
| void call(const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.call_r(op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.call_m(op.disp(), op.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| CodeOffset callWithPatch() { |
| return CodeOffset(masm.call().offset()); |
| } |
| void patchCall(uint32_t callerOffset, uint32_t calleeOffset) { |
| unsigned char* code = masm.data(); |
| X86Encoding::SetRel32(code + callerOffset, code + calleeOffset); |
| } |
| |
| void breakpoint() { |
| masm.int3(); |
| } |
| |
| static bool HasSSE2() { return CPUInfo::IsSSE2Present(); } |
| static bool HasSSE3() { return CPUInfo::IsSSE3Present(); } |
| static bool HasSSE41() { return CPUInfo::IsSSE41Present(); } |
| static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); } |
| static bool SupportsSimd() { return CPUInfo::IsSSE2Present(); } |
| static bool HasAVX() { return CPUInfo::IsAVXPresent(); } |
| |
| void cmpl(Register rhs, Register lhs) { |
| masm.cmpl_rr(rhs.encoding(), lhs.encoding()); |
| } |
| void cmpl(const Operand& rhs, Register lhs) { |
| switch (rhs.kind()) { |
| case Operand::REG: |
| masm.cmpl_rr(rhs.reg(), lhs.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.cmpl_mr(rhs.disp(), rhs.base(), lhs.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.cmpl_mr(rhs.address(), lhs.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void cmpl(Register rhs, const Operand& lhs) { |
| switch (lhs.kind()) { |
| case Operand::REG: |
| masm.cmpl_rr(rhs.encoding(), lhs.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.cmpl_rm(rhs.encoding(), lhs.disp(), lhs.base()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.cmpl_rm(rhs.encoding(), lhs.address()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void cmpl(Imm32 rhs, Register lhs) { |
| masm.cmpl_ir(rhs.value, lhs.encoding()); |
| } |
| void cmpl(Imm32 rhs, const Operand& lhs) { |
| switch (lhs.kind()) { |
| case Operand::REG: |
| masm.cmpl_ir(rhs.value, lhs.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.cmpl_im(rhs.value, lhs.disp(), lhs.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.cmpl_im(rhs.value, lhs.disp(), lhs.base(), lhs.index(), lhs.scale()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.cmpl_im(rhs.value, lhs.address()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| CodeOffset cmplWithPatch(Imm32 rhs, Register lhs) { |
| masm.cmpl_i32r(rhs.value, lhs.encoding()); |
| return CodeOffset(masm.currentOffset()); |
| } |
| void cmpw(Register rhs, Register lhs) { |
| masm.cmpw_rr(rhs.encoding(), lhs.encoding()); |
| } |
| void setCC(Condition cond, Register r) { |
| masm.setCC_r(static_cast<X86Encoding::Condition>(cond), r.encoding()); |
| } |
| void testb(Register rhs, Register lhs) { |
| MOZ_ASSERT(AllocatableGeneralRegisterSet(Registers::SingleByteRegs).has(rhs)); |
| MOZ_ASSERT(AllocatableGeneralRegisterSet(Registers::SingleByteRegs).has(lhs)); |
| masm.testb_rr(rhs.encoding(), lhs.encoding()); |
| } |
| void testw(Register rhs, Register lhs) { |
| masm.testw_rr(lhs.encoding(), rhs.encoding()); |
| } |
| void testl(Register rhs, Register lhs) { |
| masm.testl_rr(lhs.encoding(), rhs.encoding()); |
| } |
| void testl(Imm32 rhs, Register lhs) { |
| masm.testl_ir(rhs.value, lhs.encoding()); |
| } |
| void testl(Imm32 rhs, const Operand& lhs) { |
| switch (lhs.kind()) { |
| case Operand::REG: |
| masm.testl_ir(rhs.value, lhs.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.testl_i32m(rhs.value, lhs.disp(), lhs.base()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.testl_i32m(rhs.value, lhs.address()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| |
| void addl(Imm32 imm, Register dest) { |
| masm.addl_ir(imm.value, dest.encoding()); |
| } |
| CodeOffset addlWithPatch(Imm32 imm, Register dest) { |
| masm.addl_i32r(imm.value, dest.encoding()); |
| return CodeOffset(masm.currentOffset()); |
| } |
| void addl(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.addl_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.addl_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.addl_im(imm.value, op.address()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.addl_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void addw(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.addw_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.addw_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.addw_im(imm.value, op.address()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.addw_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void subl(Imm32 imm, Register dest) { |
| masm.subl_ir(imm.value, dest.encoding()); |
| } |
| void subl(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.subl_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.subl_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.subl_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void subw(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.subw_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.subw_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.subw_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void addl(Register src, Register dest) { |
| masm.addl_rr(src.encoding(), dest.encoding()); |
| } |
| void addl(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.addl_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.addl_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.addl_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void addw(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.addw_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.addw_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.addw_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void subl(Register src, Register dest) { |
| masm.subl_rr(src.encoding(), dest.encoding()); |
| } |
| void subl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.subl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.subl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void subl(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.subl_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.subl_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.subl_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void subw(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.subw_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.subw_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.subw_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void orl(Register reg, Register dest) { |
| masm.orl_rr(reg.encoding(), dest.encoding()); |
| } |
| void orl(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.orl_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.orl_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.orl_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void orw(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.orw_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.orw_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.orw_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void orl(Imm32 imm, Register reg) { |
| masm.orl_ir(imm.value, reg.encoding()); |
| } |
| void orl(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.orl_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.orl_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.orl_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void orw(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.orw_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.orw_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.orw_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xorl(Register src, Register dest) { |
| masm.xorl_rr(src.encoding(), dest.encoding()); |
| } |
| void xorl(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.xorl_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.xorl_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xorl_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xorw(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.xorw_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.xorw_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xorw_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xorl(Imm32 imm, Register reg) { |
| masm.xorl_ir(imm.value, reg.encoding()); |
| } |
| void xorl(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.xorl_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.xorl_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xorl_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xorw(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.xorw_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.xorw_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xorw_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void andl(Register src, Register dest) { |
| masm.andl_rr(src.encoding(), dest.encoding()); |
| } |
| void andl(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.andl_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.andl_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.andl_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void andw(Register src, const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.andw_rr(src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.andw_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.andw_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void andl(Imm32 imm, Register dest) { |
| masm.andl_ir(imm.value, dest.encoding()); |
| } |
| void andl(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.andl_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.andl_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.andl_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void andw(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::REG: |
| masm.andw_ir(imm.value, op.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.andw_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.andw_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void addl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.addl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.addl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void orl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.orl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.orl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xorl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.xorl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.xorl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void andl(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.andl_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.andl_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void bsr(const Register& src, const Register& dest) { |
| masm.bsr_rr(src.encoding(), dest.encoding()); |
| } |
| void imull(Register multiplier) { |
| masm.imull_r(multiplier.encoding()); |
| } |
| void umull(Register multiplier) { |
| masm.mull_r(multiplier.encoding()); |
| } |
| void imull(Imm32 imm, Register dest) { |
| masm.imull_ir(imm.value, dest.encoding(), dest.encoding()); |
| } |
| void imull(Register src, Register dest) { |
| masm.imull_rr(src.encoding(), dest.encoding()); |
| } |
| void imull(Imm32 imm, Register src, Register dest) { |
| masm.imull_ir(imm.value, src.encoding(), dest.encoding()); |
| } |
| void imull(const Operand& src, Register dest) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.imull_rr(src.reg(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.imull_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void negl(const Operand& src) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.negl_r(src.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.negl_m(src.disp(), src.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void negl(Register reg) { |
| masm.negl_r(reg.encoding()); |
| } |
| void notl(const Operand& src) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.notl_r(src.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.notl_m(src.disp(), src.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void notl(Register reg) { |
| masm.notl_r(reg.encoding()); |
| } |
| void shrl(const Imm32 imm, Register dest) { |
| masm.shrl_ir(imm.value, dest.encoding()); |
| } |
| void shll(const Imm32 imm, Register dest) { |
| masm.shll_ir(imm.value, dest.encoding()); |
| } |
| void sarl(const Imm32 imm, Register dest) { |
| masm.sarl_ir(imm.value, dest.encoding()); |
| } |
| void shrl_cl(Register dest) { |
| masm.shrl_CLr(dest.encoding()); |
| } |
| void shll_cl(Register dest) { |
| masm.shll_CLr(dest.encoding()); |
| } |
| void sarl_cl(Register dest) { |
| masm.sarl_CLr(dest.encoding()); |
| } |
| |
| void incl(const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.incl_m32(op.disp(), op.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void lock_incl(const Operand& op) { |
| masm.prefix_lock(); |
| incl(op); |
| } |
| |
| void decl(const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.decl_m32(op.disp(), op.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void lock_decl(const Operand& op) { |
| masm.prefix_lock(); |
| decl(op); |
| } |
| |
| void addb(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.addb_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.addb_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| void addb(Register src, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.addb_rm(src.encoding(), op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.addb_rm(src.encoding(), op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| |
| void subb(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.subb_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.subb_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| void subb(Register src, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.subb_rm(src.encoding(), op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.subb_rm(src.encoding(), op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| |
| void andb(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.andb_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.andb_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| void andb(Register src, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.andb_rm(src.encoding(), op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.andb_rm(src.encoding(), op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| |
| void orb(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.orb_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.orb_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| void orb(Register src, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.orb_rm(src.encoding(), op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.orb_rm(src.encoding(), op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| |
| void xorb(Imm32 imm, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.xorb_im(imm.value, op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xorb_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| void xorb(Register src, const Operand& op) { |
| switch (op.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.xorb_rm(src.encoding(), op.disp(), op.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xorb_rm(src.encoding(), op.disp(), op.base(), op.index(), op.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| break; |
| } |
| } |
| |
| template<typename T> |
| void lock_addb(T src, const Operand& op) { |
| masm.prefix_lock(); |
| addb(src, op); |
| } |
| template<typename T> |
| void lock_subb(T src, const Operand& op) { |
| masm.prefix_lock(); |
| subb(src, op); |
| } |
| template<typename T> |
| void lock_andb(T src, const Operand& op) { |
| masm.prefix_lock(); |
| andb(src, op); |
| } |
| template<typename T> |
| void lock_orb(T src, const Operand& op) { |
| masm.prefix_lock(); |
| orb(src, op); |
| } |
| template<typename T> |
| void lock_xorb(T src, const Operand& op) { |
| masm.prefix_lock(); |
| xorb(src, op); |
| } |
| |
| template<typename T> |
| void lock_addw(T src, const Operand& op) { |
| masm.prefix_lock(); |
| addw(src, op); |
| } |
| template<typename T> |
| void lock_subw(T src, const Operand& op) { |
| masm.prefix_lock(); |
| subw(src, op); |
| } |
| template<typename T> |
| void lock_andw(T src, const Operand& op) { |
| masm.prefix_lock(); |
| andw(src, op); |
| } |
| template<typename T> |
| void lock_orw(T src, const Operand& op) { |
| masm.prefix_lock(); |
| orw(src, op); |
| } |
| template<typename T> |
| void lock_xorw(T src, const Operand& op) { |
| masm.prefix_lock(); |
| xorw(src, op); |
| } |
| |
| // Note, lock_addl(imm, op) is used for a memory barrier on non-SSE2 systems, |
| // among other things. Do not optimize, replace by XADDL, or similar. |
| template<typename T> |
| void lock_addl(T src, const Operand& op) { |
| masm.prefix_lock(); |
| addl(src, op); |
| } |
| template<typename T> |
| void lock_subl(T src, const Operand& op) { |
| masm.prefix_lock(); |
| subl(src, op); |
| } |
| template<typename T> |
| void lock_andl(T src, const Operand& op) { |
| masm.prefix_lock(); |
| andl(src, op); |
| } |
| template<typename T> |
| void lock_orl(T src, const Operand& op) { |
| masm.prefix_lock(); |
| orl(src, op); |
| } |
| template<typename T> |
| void lock_xorl(T src, const Operand& op) { |
| masm.prefix_lock(); |
| xorl(src, op); |
| } |
| |
| void lock_cmpxchgb(Register src, const Operand& mem) { |
| masm.prefix_lock(); |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.cmpxchgb(src.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.cmpxchgb(src.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void lock_cmpxchgw(Register src, const Operand& mem) { |
| masm.prefix_lock(); |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.cmpxchgw(src.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.cmpxchgw(src.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void lock_cmpxchgl(Register src, const Operand& mem) { |
| masm.prefix_lock(); |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.cmpxchgl(src.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.cmpxchgl(src.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| void xchgb(Register src, const Operand& mem) { |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.xchgb_rm(src.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xchgb_rm(src.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xchgw(Register src, const Operand& mem) { |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.xchgw_rm(src.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xchgw_rm(src.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void xchgl(Register src, const Operand& mem) { |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.xchgl_rm(src.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.xchgl_rm(src.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| void lock_xaddb(Register srcdest, const Operand& mem) { |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.lock_xaddb_rm(srcdest.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.lock_xaddb_rm(srcdest.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void lock_xaddw(Register srcdest, const Operand& mem) { |
| masm.prefix_16_for_32(); |
| lock_xaddl(srcdest, mem); |
| } |
| void lock_xaddl(Register srcdest, const Operand& mem) { |
| switch (mem.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.lock_xaddl_rm(srcdest.encoding(), mem.disp(), mem.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.lock_xaddl_rm(srcdest.encoding(), mem.disp(), mem.base(), mem.index(), mem.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| void push(const Imm32 imm) { |
| masm.push_i(imm.value); |
| } |
| |
| void push(const Operand& src) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.push_r(src.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.push_m(src.disp(), src.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void push(Register src) { |
| masm.push_r(src.encoding()); |
| } |
| void push(const Address& src) { |
| masm.push_m(src.offset, src.base.encoding()); |
| } |
| |
| void pop(const Operand& src) { |
| switch (src.kind()) { |
| case Operand::REG: |
| masm.pop_r(src.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.pop_m(src.disp(), src.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void pop(Register src) { |
| masm.pop_r(src.encoding()); |
| } |
| void pop(const Address& src) { |
| masm.pop_m(src.offset, src.base.encoding()); |
| } |
| |
| void pushFlags() { |
| masm.push_flags(); |
| } |
| void popFlags() { |
| masm.pop_flags(); |
| } |
| |
| #ifdef JS_CODEGEN_X86 |
| void pushAllRegs() { |
| masm.pusha(); |
| } |
| void popAllRegs() { |
| masm.popa(); |
| } |
| #endif |
| |
| // Zero-extend byte to 32-bit integer. |
| void movzbl(Register src, Register dest) { |
| masm.movzbl_rr(src.encoding(), dest.encoding()); |
| } |
| |
| void cdq() { |
| masm.cdq(); |
| } |
| void idiv(Register divisor) { |
| masm.idivl_r(divisor.encoding()); |
| } |
| void udiv(Register divisor) { |
| masm.divl_r(divisor.encoding()); |
| } |
| |
| void vpinsrd(unsigned lane, Register src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vpinsrd_irr(lane, src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpinsrd(unsigned lane, const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| switch (src1.kind()) { |
| case Operand::REG: |
| masm.vpinsrd_irr(lane, src1.reg(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpinsrd_imr(lane, src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpextrd(unsigned lane, FloatRegister src, Register dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vpextrd_irr(lane, src.encoding(), dest.encoding()); |
| } |
| void vpextrd(unsigned lane, FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE41()); |
| switch (dest.kind()) { |
| case Operand::REG: |
| masm.vpextrd_irr(lane, src.encoding(), dest.reg()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpextrd_irm(lane, src.encoding(), dest.disp(), dest.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpsrldq(Imm32 shift, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsrldq_ir(shift.value, src0.encoding(), dest.encoding()); |
| } |
| void vpsllq(Imm32 shift, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsllq_ir(shift.value, src0.encoding(), dest.encoding()); |
| } |
| void vpsrlq(Imm32 shift, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsrlq_ir(shift.value, src0.encoding(), dest.encoding()); |
| } |
| void vpslld(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpslld_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpslld(Imm32 count, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpslld_ir(count.value, src0.encoding(), dest.encoding()); |
| } |
| void vpsrad(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsrad_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpsrad(Imm32 count, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsrad_ir(count.value, src0.encoding(), dest.encoding()); |
| } |
| void vpsrld(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsrld_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpsrld(Imm32 count, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpsrld_ir(count.value, src0.encoding(), dest.encoding()); |
| } |
| |
| void vcvtsi2sd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::REG: |
| masm.vcvtsi2sd_rr(src1.reg(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vcvtsi2sd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vcvtsi2sd_mr(src1.disp(), src1.base(), src1.index(), src1.scale(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vcvttsd2si(FloatRegister src, Register dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvttsd2si_rr(src.encoding(), dest.encoding()); |
| } |
| void vcvttss2si(FloatRegister src, Register dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvttss2si_rr(src.encoding(), dest.encoding()); |
| } |
| void vcvtsi2ss(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::REG: |
| masm.vcvtsi2ss_rr(src1.reg(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vcvtsi2ss_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vcvtsi2ss_mr(src1.disp(), src1.base(), src1.index(), src1.scale(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vcvtsi2ss(Register src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvtsi2ss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vcvtsi2sd(Register src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvtsi2sd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vcvttps2dq(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvttps2dq_rr(src.encoding(), dest.encoding()); |
| } |
| void vcvtdq2ps(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vcvtdq2ps_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovmskpd(FloatRegister src, Register dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovmskpd_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovmskps(FloatRegister src, Register dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovmskps_rr(src.encoding(), dest.encoding()); |
| } |
| void vptest(FloatRegister rhs, FloatRegister lhs) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vptest_rr(rhs.encoding(), lhs.encoding()); |
| } |
| void vucomisd(FloatRegister rhs, FloatRegister lhs) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vucomisd_rr(rhs.encoding(), lhs.encoding()); |
| } |
| void vucomiss(FloatRegister rhs, FloatRegister lhs) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vucomiss_rr(rhs.encoding(), lhs.encoding()); |
| } |
| void vpcmpeqw(FloatRegister rhs, FloatRegister lhs, FloatRegister dst) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpcmpeqw_rr(rhs.encoding(), lhs.encoding(), dst.encoding()); |
| } |
| void vpcmpeqd(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (rhs.kind()) { |
| case Operand::FPREG: |
| masm.vpcmpeqd_rr(rhs.fpu(), lhs.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpcmpeqd_mr(rhs.disp(), rhs.base(), lhs.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpcmpeqd_mr(rhs.address(), lhs.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpcmpgtd(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (rhs.kind()) { |
| case Operand::FPREG: |
| masm.vpcmpgtd_rr(rhs.fpu(), lhs.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpcmpgtd_mr(rhs.disp(), rhs.base(), lhs.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpcmpgtd_mr(rhs.address(), lhs.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vcmpps(uint8_t order, Operand src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| // :TODO: (Bug 1132894) See LIRGeneratorX86Shared::lowerForFPU |
| // FIXME: This logic belongs in the MacroAssembler. |
| if (!HasAVX() && !src0.aliases(dest)) { |
| if (src1.kind() == Operand::FPREG && |
| dest.aliases(FloatRegister::FromCode(src1.fpu()))) |
| { |
| vmovdqa(src1, ScratchSimd128Reg); |
| src1 = Operand(ScratchSimd128Reg); |
| } |
| vmovdqa(src0, dest); |
| src0 = dest; |
| } |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vcmpps_rr(order, src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vcmpps_mr(order, src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vcmpps_mr(order, src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vcmpeqps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| vcmpps(X86Encoding::ConditionCmp_EQ, src1, src0, dest); |
| } |
| void vcmpltps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| vcmpps(X86Encoding::ConditionCmp_LT, src1, src0, dest); |
| } |
| void vcmpleps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| vcmpps(X86Encoding::ConditionCmp_LE, src1, src0, dest); |
| } |
| void vcmpunordps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| vcmpps(X86Encoding::ConditionCmp_UNORD, src1, src0, dest); |
| } |
| void vcmpneqps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| vcmpps(X86Encoding::ConditionCmp_NEQ, src1, src0, dest); |
| } |
| void vrcpps(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::FPREG: |
| masm.vrcpps_rr(src.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vrcpps_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vrcpps_mr(src.address(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vsqrtps(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::FPREG: |
| masm.vsqrtps_rr(src.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vsqrtps_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vsqrtps_mr(src.address(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vrsqrtps(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::FPREG: |
| masm.vrsqrtps_rr(src.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vrsqrtps_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vrsqrtps_mr(src.address(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovd(Register src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovd_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovd(FloatRegister src, Register dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovd_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovd(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovd_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovd_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovd(FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovd_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovd_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vmovq_rm(src.encoding(), dest.address()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovq(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovq_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vmovq_mr(src.address(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovq(FloatRegister src, const Operand& dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.vmovq_rm(src.encoding(), dest.disp(), dest.base()); |
| break; |
| case Operand::MEM_SCALE: |
| masm.vmovq_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpaddd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpaddd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpaddd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpaddd_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpsubd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpsubd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpsubd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpsubd_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpmuludq(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpmuludq_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpmuludq(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpmuludq_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpmuludq_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpmulld(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpmulld_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpmulld_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpmulld_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vaddps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vaddps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vaddps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vaddps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vsubps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vsubps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vsubps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vsubps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmulps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vmulps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmulps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vmulps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vdivps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vdivps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vdivps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vdivps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmaxps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vmaxps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmaxps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vmaxps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vminps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vminps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vminps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vminps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vandps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vandps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vandps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vandps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vandnps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| // Negates bits of dest and then applies AND |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vandnps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vandnps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vandnps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vorps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vorps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vorps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vorps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vxorps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vxorps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vxorps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vxorps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpand(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpand_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpand(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpand_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpand_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpand_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpor(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpor_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpor(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpor_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpor_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpor_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpxor(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpxor_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpxor(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpxor_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpxor_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpxor_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vpandn(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpandn_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vpandn(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpandn_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpandn_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpandn_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| void vpshufd(uint32_t mask, FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vpshufd_irr(mask, src.encoding(), dest.encoding()); |
| } |
| void vpshufd(uint32_t mask, const Operand& src1, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vpshufd_irr(mask, src1.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vpshufd_imr(mask, src1.disp(), src1.base(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vpshufd_imr(mask, src1.address(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovddup(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE3()); |
| masm.vmovddup_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovhlps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovhlps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vmovlhps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmovlhps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vunpcklps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vunpcklps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vunpcklps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vunpcklps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vunpcklps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vunpcklps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vunpckhps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vunpckhps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vunpckhps(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vunpckhps_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vunpckhps_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vunpckhps_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vshufps(uint32_t mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vshufps_irr(mask, src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vshufps(uint32_t mask, const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vshufps_irr(mask, src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vshufps_imr(mask, src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vshufps_imr(mask, src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vaddsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vaddsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vaddss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vaddss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vaddsd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vaddsd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vaddsd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vaddsd_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vaddss(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vaddss_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vaddss_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_ADDRESS32: |
| masm.vaddss_mr(src1.address(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vsubsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vsubsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vsubss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vsubss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vsubsd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vsubsd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vsubsd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vsubss(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vsubss_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vsubss_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmulsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmulsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vmulsd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vmulsd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmulsd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmulss(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vmulss_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmulss_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmulss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmulss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vdivsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vdivsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vdivss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vdivss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vdivsd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vdivsd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vdivsd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vdivss(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vdivss_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vdivss_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vxorpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vxorpd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vxorps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vxorps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vorpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vorpd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vorps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vorps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vandpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vandpd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vandps(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vandps_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vsqrtsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vsqrtsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vsqrtss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vsqrtss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vroundsd(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vroundsd_irr(mode, src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vroundss(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vroundss_irr(mode, src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| unsigned vinsertpsMask(SimdLane sourceLane, SimdLane destLane, unsigned zeroMask = 0) |
| { |
| // Note that the sourceLane bits are ignored in the case of a source |
| // memory operand, and the source is the given 32-bits memory location. |
| MOZ_ASSERT(zeroMask < 16); |
| unsigned ret = zeroMask ; |
| ret |= unsigned(destLane) << 4; |
| ret |= unsigned(sourceLane) << 6; |
| MOZ_ASSERT(ret < 256); |
| return ret; |
| } |
| void vinsertps(uint32_t mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vinsertps_irr(mask, src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vinsertps(uint32_t mask, const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vinsertps_irr(mask, src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vinsertps_imr(mask, src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| unsigned blendpsMask(bool x, bool y, bool z, bool w) { |
| return (x << 0) | (y << 1) | (z << 2) | (w << 3); |
| } |
| void vblendps(unsigned mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vblendps_irr(mask, src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vblendps(unsigned mask, const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vblendps_irr(mask, src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vblendps_imr(mask, src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vblendvps(FloatRegister mask, FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| masm.vblendvps_rr(mask.encoding(), src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vblendvps(FloatRegister mask, const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE41()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vblendvps_rr(mask.encoding(), src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vblendvps_mr(mask.encoding(), src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovsldup(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE3()); |
| masm.vmovsldup_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovsldup(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE3()); |
| switch (src.kind()) { |
| case Operand::FPREG: |
| masm.vmovsldup_rr(src.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmovsldup_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmovshdup(FloatRegister src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE3()); |
| masm.vmovshdup_rr(src.encoding(), dest.encoding()); |
| } |
| void vmovshdup(const Operand& src, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE3()); |
| switch (src.kind()) { |
| case Operand::FPREG: |
| masm.vmovshdup_rr(src.fpu(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmovshdup_mr(src.disp(), src.base(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vminsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vminsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vminsd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vminsd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vminsd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vminss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vminss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vmaxsd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmaxsd_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void vmaxsd(const Operand& src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| switch (src1.kind()) { |
| case Operand::FPREG: |
| masm.vmaxsd_rr(src1.fpu(), src0.encoding(), dest.encoding()); |
| break; |
| case Operand::MEM_REG_DISP: |
| masm.vmaxsd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void vmaxss(FloatRegister src1, FloatRegister src0, FloatRegister dest) { |
| MOZ_ASSERT(HasSSE2()); |
| masm.vmaxss_rr(src1.encoding(), src0.encoding(), dest.encoding()); |
| } |
| void fisttp(const Operand& dest) { |
| MOZ_ASSERT(HasSSE3()); |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.fisttp_m(dest.disp(), dest.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void fld(const Operand& dest) { |
| switch (dest.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.fld_m(dest.disp(), dest.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void fstp(const Operand& src) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.fstp_m(src.disp(), src.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| void fstp32(const Operand& src) { |
| switch (src.kind()) { |
| case Operand::MEM_REG_DISP: |
| masm.fstp32_m(src.disp(), src.base()); |
| break; |
| default: |
| MOZ_CRASH("unexpected operand kind"); |
| } |
| } |
| |
| // Defined for compatibility with ARM's assembler |
| uint32_t actualIndex(uint32_t x) { |
| return x; |
| } |
| |
| void flushBuffer() { |
| } |
| |
| // Patching. |
| |
| static size_t PatchWrite_NearCallSize() { |
| return 5; |
| } |
| static uintptr_t GetPointer(uint8_t* instPtr) { |
| uintptr_t* ptr = ((uintptr_t*) instPtr) - 1; |
| return *ptr; |
| } |
| // Write a relative call at the start location |dataLabel|. |
| // Note that this DOES NOT patch data that comes before |label|. |
| static void PatchWrite_NearCall(CodeLocationLabel startLabel, CodeLocationLabel target) { |
| uint8_t* start = startLabel.raw(); |
| *start = 0xE8; |
| ptrdiff_t offset = target - startLabel - PatchWrite_NearCallSize(); |
| MOZ_ASSERT(int32_t(offset) == offset); |
| *((int32_t*) (start + 1)) = offset; |
| } |
| |
| static void PatchWrite_Imm32(CodeLocationLabel dataLabel, Imm32 toWrite) { |
| *((int32_t*) dataLabel.raw() - 1) = toWrite.value; |
| } |
| |
| static void PatchDataWithValueCheck(CodeLocationLabel data, PatchedImmPtr newData, |
| PatchedImmPtr expectedData) { |
| // The pointer given is a pointer to *after* the data. |
| uintptr_t* ptr = ((uintptr_t*) data.raw()) - 1; |
| MOZ_ASSERT(*ptr == (uintptr_t)expectedData.value); |
| *ptr = (uintptr_t)newData.value; |
| } |
| static void PatchDataWithValueCheck(CodeLocationLabel data, ImmPtr newData, ImmPtr expectedData) { |
| PatchDataWithValueCheck(data, PatchedImmPtr(newData.value), PatchedImmPtr(expectedData.value)); |
| } |
| |
| static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) { |
| MOZ_CRASH("Unused."); |
| } |
| |
| static uint32_t NopSize() { |
| return 1; |
| } |
| static uint8_t* NextInstruction(uint8_t* cur, uint32_t* count) { |
| MOZ_CRASH("nextInstruction NYI on x86"); |
| } |
| |
| // Toggle a jmp or cmp emitted by toggledJump(). |
| static void ToggleToJmp(CodeLocationLabel inst) { |
| uint8_t* ptr = (uint8_t*)inst.raw(); |
| MOZ_ASSERT(*ptr == 0x3D); |
| *ptr = 0xE9; |
| } |
| static void ToggleToCmp(CodeLocationLabel inst) { |
| uint8_t* ptr = (uint8_t*)inst.raw(); |
| MOZ_ASSERT(*ptr == 0xE9); |
| *ptr = 0x3D; |
| } |
| static void ToggleCall(CodeLocationLabel inst, bool enabled) { |
| uint8_t* ptr = (uint8_t*)inst.raw(); |
| MOZ_ASSERT(*ptr == 0x3D || // CMP |
| *ptr == 0xE8); // CALL |
| *ptr = enabled ? 0xE8 : 0x3D; |
| } |
| |
| MOZ_COLD void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, |
| const Disassembler::HeapAccess& heapAccess); |
| }; |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* jit_x86_shared_Assembler_x86_shared_h */ |