michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_shared_Assembler_x86_shared_h michael@0: #define jit_shared_Assembler_x86_shared_h michael@0: michael@0: #include michael@0: michael@0: #include "assembler/assembler/X86Assembler.h" michael@0: #include "jit/shared/Assembler-shared.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: class Operand michael@0: { michael@0: public: michael@0: enum Kind { michael@0: REG, michael@0: MEM_REG_DISP, michael@0: FPREG, michael@0: MEM_SCALE, michael@0: MEM_ADDRESS32 michael@0: }; michael@0: michael@0: private: michael@0: Kind kind_ : 4; michael@0: int32_t base_ : 5; michael@0: Scale scale_ : 3; michael@0: int32_t index_ : 5; michael@0: int32_t disp_; michael@0: michael@0: public: michael@0: explicit Operand(Register reg) michael@0: : kind_(REG), michael@0: base_(reg.code()) michael@0: { } michael@0: explicit Operand(FloatRegister reg) michael@0: : kind_(FPREG), michael@0: base_(reg.code()) michael@0: { } michael@0: explicit Operand(const Address &address) michael@0: : kind_(MEM_REG_DISP), michael@0: base_(address.base.code()), michael@0: disp_(address.offset) michael@0: { } michael@0: explicit Operand(const BaseIndex &address) michael@0: : kind_(MEM_SCALE), michael@0: base_(address.base.code()), michael@0: scale_(address.scale), michael@0: index_(address.index.code()), michael@0: disp_(address.offset) michael@0: { } michael@0: Operand(Register base, Register index, Scale scale, int32_t disp = 0) michael@0: : kind_(MEM_SCALE), michael@0: base_(base.code()), michael@0: scale_(scale), michael@0: index_(index.code()), michael@0: disp_(disp) michael@0: { } michael@0: Operand(Register reg, int32_t disp) michael@0: : kind_(MEM_REG_DISP), michael@0: base_(reg.code()), michael@0: disp_(disp) michael@0: { } michael@0: explicit Operand(const AbsoluteAddress &address) michael@0: : kind_(MEM_ADDRESS32), michael@0: disp_(JSC::X86Assembler::addressImmediate(address.addr)) michael@0: { } michael@0: michael@0: Address toAddress() const { michael@0: JS_ASSERT(kind() == MEM_REG_DISP); michael@0: return Address(Register::FromCode(base()), disp()); michael@0: } michael@0: michael@0: BaseIndex toBaseIndex() const { michael@0: JS_ASSERT(kind() == MEM_SCALE); michael@0: return BaseIndex(Register::FromCode(base()), Register::FromCode(index()), scale(), disp()); michael@0: } michael@0: michael@0: Kind kind() const { michael@0: return kind_; michael@0: } michael@0: Registers::Code reg() const { michael@0: JS_ASSERT(kind() == REG); michael@0: return (Registers::Code)base_; michael@0: } michael@0: Registers::Code base() const { michael@0: JS_ASSERT(kind() == MEM_REG_DISP || kind() == MEM_SCALE); michael@0: return (Registers::Code)base_; michael@0: } michael@0: Registers::Code index() const { michael@0: JS_ASSERT(kind() == MEM_SCALE); michael@0: return (Registers::Code)index_; michael@0: } michael@0: Scale scale() const { michael@0: JS_ASSERT(kind() == MEM_SCALE); michael@0: return scale_; michael@0: } michael@0: FloatRegisters::Code fpu() const { michael@0: JS_ASSERT(kind() == FPREG); michael@0: return (FloatRegisters::Code)base_; michael@0: } michael@0: int32_t disp() const { michael@0: JS_ASSERT(kind() == MEM_REG_DISP || kind() == MEM_SCALE); michael@0: return disp_; michael@0: } michael@0: void *address() const { michael@0: JS_ASSERT(kind() == MEM_ADDRESS32); michael@0: return reinterpret_cast(disp_); michael@0: } michael@0: }; michael@0: michael@0: class AssemblerX86Shared : public AssemblerShared michael@0: { michael@0: protected: michael@0: struct RelativePatch { michael@0: int32_t offset; michael@0: void *target; michael@0: Relocation::Kind kind; michael@0: michael@0: RelativePatch(int32_t offset, void *target, Relocation::Kind kind) michael@0: : offset(offset), michael@0: target(target), michael@0: kind(kind) michael@0: { } michael@0: }; michael@0: michael@0: Vector codeLabels_; michael@0: Vector jumps_; michael@0: CompactBufferWriter jumpRelocations_; michael@0: CompactBufferWriter dataRelocations_; michael@0: CompactBufferWriter preBarriers_; michael@0: bool enoughMemory_; michael@0: michael@0: void writeDataRelocation(const Value &val) { michael@0: if (val.isMarkable()) { michael@0: JS_ASSERT(static_cast(val.toGCThing())->isTenured()); michael@0: dataRelocations_.writeUnsigned(masm.currentOffset()); michael@0: } michael@0: } michael@0: void writeDataRelocation(const ImmGCPtr &ptr) { michael@0: if (ptr.value) michael@0: dataRelocations_.writeUnsigned(masm.currentOffset()); michael@0: } michael@0: void writePrebarrierOffset(CodeOffsetLabel label) { michael@0: preBarriers_.writeUnsigned(label.offset()); michael@0: } michael@0: michael@0: protected: michael@0: JSC::X86Assembler masm; michael@0: michael@0: typedef JSC::X86Assembler::JmpSrc JmpSrc; michael@0: typedef JSC::X86Assembler::JmpDst JmpDst; michael@0: michael@0: public: michael@0: enum Condition { michael@0: Equal = JSC::X86Assembler::ConditionE, michael@0: NotEqual = JSC::X86Assembler::ConditionNE, michael@0: Above = JSC::X86Assembler::ConditionA, michael@0: AboveOrEqual = JSC::X86Assembler::ConditionAE, michael@0: Below = JSC::X86Assembler::ConditionB, michael@0: BelowOrEqual = JSC::X86Assembler::ConditionBE, michael@0: GreaterThan = JSC::X86Assembler::ConditionG, michael@0: GreaterThanOrEqual = JSC::X86Assembler::ConditionGE, michael@0: LessThan = JSC::X86Assembler::ConditionL, michael@0: LessThanOrEqual = JSC::X86Assembler::ConditionLE, michael@0: Overflow = JSC::X86Assembler::ConditionO, michael@0: Signed = JSC::X86Assembler::ConditionS, michael@0: NotSigned = JSC::X86Assembler::ConditionNS, michael@0: Zero = JSC::X86Assembler::ConditionE, michael@0: NonZero = JSC::X86Assembler::ConditionNE, michael@0: Parity = JSC::X86Assembler::ConditionP, michael@0: NoParity = JSC::X86Assembler::ConditionNP michael@0: }; michael@0: michael@0: // If this bit is set, the ucomisd operands have to be inverted. michael@0: static const int DoubleConditionBitInvert = 0x10; michael@0: michael@0: // Bit set when a DoubleCondition does not map to a single x86 condition. michael@0: // The macro assembler has to special-case these conditions. michael@0: static const int DoubleConditionBitSpecial = 0x20; michael@0: static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; michael@0: michael@0: enum DoubleCondition { michael@0: // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. michael@0: DoubleOrdered = NoParity, michael@0: DoubleEqual = Equal | DoubleConditionBitSpecial, michael@0: DoubleNotEqual = NotEqual, michael@0: DoubleGreaterThan = Above, michael@0: DoubleGreaterThanOrEqual = AboveOrEqual, michael@0: DoubleLessThan = Above | DoubleConditionBitInvert, michael@0: DoubleLessThanOrEqual = AboveOrEqual | DoubleConditionBitInvert, michael@0: // If either operand is NaN, these conditions always evaluate to true. michael@0: DoubleUnordered = Parity, michael@0: DoubleEqualOrUnordered = Equal, michael@0: DoubleNotEqualOrUnordered = NotEqual | DoubleConditionBitSpecial, michael@0: DoubleGreaterThanOrUnordered = Below | DoubleConditionBitInvert, michael@0: DoubleGreaterThanOrEqualOrUnordered = BelowOrEqual | DoubleConditionBitInvert, michael@0: DoubleLessThanOrUnordered = Below, michael@0: DoubleLessThanOrEqualOrUnordered = BelowOrEqual michael@0: }; michael@0: michael@0: enum NaNCond { michael@0: NaN_HandledByCond, michael@0: NaN_IsTrue, michael@0: NaN_IsFalse michael@0: }; michael@0: michael@0: // If the primary condition returned by ConditionFromDoubleCondition doesn't michael@0: // handle NaNs properly, return NaN_IsFalse if the comparison should be michael@0: // overridden to return false on NaN, NaN_IsTrue if it should be overridden michael@0: // to return true on NaN, or NaN_HandledByCond if no secondary check is michael@0: // needed. michael@0: static inline NaNCond NaNCondFromDoubleCondition(DoubleCondition cond) { michael@0: switch (cond) { michael@0: case DoubleOrdered: michael@0: case DoubleNotEqual: michael@0: case DoubleGreaterThan: michael@0: case DoubleGreaterThanOrEqual: michael@0: case DoubleLessThan: michael@0: case DoubleLessThanOrEqual: michael@0: case DoubleUnordered: michael@0: case DoubleEqualOrUnordered: michael@0: case DoubleGreaterThanOrUnordered: michael@0: case DoubleGreaterThanOrEqualOrUnordered: michael@0: case DoubleLessThanOrUnordered: michael@0: case DoubleLessThanOrEqualOrUnordered: michael@0: return NaN_HandledByCond; michael@0: case DoubleEqual: michael@0: return NaN_IsFalse; michael@0: case DoubleNotEqualOrUnordered: michael@0: return NaN_IsTrue; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown double condition"); michael@0: } michael@0: michael@0: static void staticAsserts() { michael@0: // DoubleConditionBits should not interfere with x86 condition codes. michael@0: JS_STATIC_ASSERT(!((Equal | NotEqual | Above | AboveOrEqual | Below | michael@0: BelowOrEqual | Parity | NoParity) & DoubleConditionBits)); michael@0: } michael@0: michael@0: AssemblerX86Shared() michael@0: : enoughMemory_(true) michael@0: { michael@0: } michael@0: michael@0: static Condition InvertCondition(Condition cond); michael@0: michael@0: // Return the primary condition to test. Some primary conditions may not michael@0: // handle NaNs properly and may therefore require a secondary condition. michael@0: // Use NaNCondFromDoubleCondition to determine what else is needed. michael@0: static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) { michael@0: return static_cast(cond & ~DoubleConditionBits); michael@0: } michael@0: michael@0: static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); michael@0: michael@0: // MacroAssemblers hold onto gcthings, so they are traced by the GC. michael@0: void trace(JSTracer *trc); michael@0: michael@0: bool oom() const { michael@0: return masm.oom() || michael@0: !enoughMemory_ || michael@0: jumpRelocations_.oom() || michael@0: dataRelocations_.oom() || michael@0: preBarriers_.oom(); michael@0: } michael@0: michael@0: void setPrinter(Sprinter *sp) { michael@0: masm.setPrinter(sp); michael@0: } michael@0: michael@0: void executableCopy(void *buffer); michael@0: void processCodeLabels(uint8_t *rawCode); michael@0: void copyJumpRelocationTable(uint8_t *dest); michael@0: void copyDataRelocationTable(uint8_t *dest); michael@0: void copyPreBarrierTable(uint8_t *dest); michael@0: michael@0: bool addCodeLabel(CodeLabel label) { michael@0: return codeLabels_.append(label); michael@0: } michael@0: size_t numCodeLabels() const { michael@0: return codeLabels_.length(); michael@0: } michael@0: CodeLabel codeLabel(size_t i) { michael@0: return codeLabels_[i]; michael@0: } michael@0: michael@0: // Size of the instruction stream, in bytes. michael@0: size_t size() const { michael@0: return masm.size(); michael@0: } michael@0: // Size of the jump relocation table, in bytes. michael@0: size_t jumpRelocationTableBytes() const { michael@0: return jumpRelocations_.length(); michael@0: } michael@0: size_t dataRelocationTableBytes() const { michael@0: return dataRelocations_.length(); michael@0: } michael@0: size_t preBarrierTableBytes() const { michael@0: return preBarriers_.length(); michael@0: } michael@0: // Size of the data table, in bytes. michael@0: size_t bytesNeeded() const { michael@0: return size() + michael@0: jumpRelocationTableBytes() + michael@0: dataRelocationTableBytes() + michael@0: preBarrierTableBytes(); michael@0: } michael@0: michael@0: public: michael@0: void align(int alignment) { michael@0: masm.align(alignment); michael@0: } michael@0: void writeCodePointer(AbsoluteLabel *label) { michael@0: JS_ASSERT(!label->bound()); michael@0: // Thread the patch list through the unpatched address word in the michael@0: // instruction stream. michael@0: masm.jumpTablePointer(label->prev()); michael@0: label->setPrev(masm.size()); michael@0: } michael@0: void writeDoubleConstant(double d, Label *label) { michael@0: label->bind(masm.size()); michael@0: masm.doubleConstant(d); michael@0: } michael@0: void writeFloatConstant(float f, Label *label) { michael@0: label->bind(masm.size()); michael@0: masm.floatConstant(f); michael@0: } michael@0: void movl(const Imm32 &imm32, const Register &dest) { michael@0: masm.movl_i32r(imm32.value, dest.code()); michael@0: } michael@0: void movl(const Register &src, const Register &dest) { michael@0: masm.movl_rr(src.code(), dest.code()); michael@0: } michael@0: void movl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.movl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.movl_mr(src.address(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movl(const Register &src, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::REG: michael@0: masm.movl_rr(src.code(), dest.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movl_rm(src.code(), dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movl_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.movl_rm(src.code(), dest.address()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movl(const Imm32 &imm32, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::REG: michael@0: masm.movl_i32r(imm32.value, dest.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movl_i32m(imm32.value, dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movl_i32m(imm32.value, dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: michael@0: void xchgl(const Register &src, const Register &dest) { michael@0: masm.xchgl_rr(src.code(), dest.code()); michael@0: } michael@0: michael@0: // Eventually movapd and movaps should be overloaded to support loads and michael@0: // stores too. michael@0: void movapd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.movapd_rr(src.code(), dest.code()); michael@0: } michael@0: void movaps(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.movaps_rr(src.code(), dest.code()); michael@0: } michael@0: michael@0: // movsd and movss are only provided in load/store form since the michael@0: // register-to-register form has different semantics (it doesn't clobber michael@0: // the whole output register) and isn't needed currently. michael@0: void movsd(const Address &src, const FloatRegister &dest) { michael@0: masm.movsd_mr(src.offset, src.base.code(), dest.code()); michael@0: } michael@0: void movsd(const BaseIndex &src, const FloatRegister &dest) { michael@0: masm.movsd_mr(src.offset, src.base.code(), src.index.code(), src.scale, dest.code()); michael@0: } michael@0: void movsd(const FloatRegister &src, const Address &dest) { michael@0: masm.movsd_rm(src.code(), dest.offset, dest.base.code()); michael@0: } michael@0: void movsd(const FloatRegister &src, const BaseIndex &dest) { michael@0: masm.movsd_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale); michael@0: } michael@0: void movss(const Address &src, const FloatRegister &dest) { michael@0: masm.movss_mr(src.offset, src.base.code(), dest.code()); michael@0: } michael@0: void movss(const BaseIndex &src, const FloatRegister &dest) { michael@0: masm.movss_mr(src.offset, src.base.code(), src.index.code(), src.scale, dest.code()); michael@0: } michael@0: void movss(const FloatRegister &src, const Address &dest) { michael@0: masm.movss_rm(src.code(), dest.offset, dest.base.code()); michael@0: } michael@0: void movss(const FloatRegister &src, const BaseIndex &dest) { michael@0: masm.movss_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale); michael@0: } michael@0: void movdqa(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movdqa_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movdqa_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movdqa(const FloatRegister &src, const Operand &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movdqa_rm(src.code(), dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movdqa_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cvtss2sd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.cvtss2sd_rr(src.code(), dest.code()); michael@0: } michael@0: void cvtsd2ss(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.cvtsd2ss_rr(src.code(), dest.code()); michael@0: } michael@0: void movzbl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movzbl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movzbl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movsbl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movsbl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movsbl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movb(const Register &src, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movb_rm(src.code(), dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movb_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movb(const Imm32 &src, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movb_i8m(src.value, dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movb_i8m(src.value, dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movzwl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.movzwl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movzwl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movzwl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movzwl(const Register &src, const Register &dest) { michael@0: masm.movzwl_rr(src.code(), dest.code()); michael@0: } michael@0: void movw(const Register &src, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movw_rm(src.code(), dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movw_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movw(const Imm32 &src, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movw_i16m(src.value, dest.disp(), dest.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movw_i16m(src.value, dest.disp(), dest.base(), dest.index(), dest.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void movswl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.movswl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.movswl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void leal(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.leal_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.leal_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: JmpSrc jSrc(Condition cond, Label *label) { michael@0: JmpSrc j = masm.jCC(static_cast(cond)); michael@0: if (label->bound()) { michael@0: // The jump can be immediately patched to the correct destination. michael@0: masm.linkJump(j, JmpDst(label->offset())); michael@0: } else { michael@0: // Thread the jump list through the unpatched jump targets. michael@0: JmpSrc prev = JmpSrc(label->use(j.offset())); michael@0: masm.setNextJump(j, prev); michael@0: } michael@0: return j; michael@0: } michael@0: JmpSrc jmpSrc(Label *label) { michael@0: JmpSrc j = masm.jmp(); michael@0: if (label->bound()) { michael@0: // The jump can be immediately patched to the correct destination. michael@0: masm.linkJump(j, JmpDst(label->offset())); michael@0: } else { michael@0: // Thread the jump list through the unpatched jump targets. michael@0: JmpSrc prev = JmpSrc(label->use(j.offset())); michael@0: masm.setNextJump(j, prev); michael@0: } michael@0: return j; michael@0: } michael@0: michael@0: // Comparison of EAX against the address given by a Label. michael@0: JmpSrc cmpSrc(Label *label) { michael@0: JmpSrc j = masm.cmp_eax(); michael@0: if (label->bound()) { michael@0: // The jump can be immediately patched to the correct destination. michael@0: masm.linkJump(j, JmpDst(label->offset())); michael@0: } else { michael@0: // Thread the jump list through the unpatched jump targets. michael@0: JmpSrc prev = JmpSrc(label->use(j.offset())); michael@0: masm.setNextJump(j, prev); michael@0: } michael@0: return j; michael@0: } michael@0: michael@0: JmpSrc jSrc(Condition cond, RepatchLabel *label) { michael@0: JmpSrc j = masm.jCC(static_cast(cond)); michael@0: if (label->bound()) { michael@0: // The jump can be immediately patched to the correct destination. michael@0: masm.linkJump(j, JmpDst(label->offset())); michael@0: } else { michael@0: label->use(j.offset()); michael@0: } michael@0: return j; michael@0: } michael@0: JmpSrc jmpSrc(RepatchLabel *label) { michael@0: JmpSrc j = masm.jmp(); michael@0: if (label->bound()) { michael@0: // The jump can be immediately patched to the correct destination. michael@0: masm.linkJump(j, JmpDst(label->offset())); michael@0: } else { michael@0: // Thread the jump list through the unpatched jump targets. michael@0: label->use(j.offset()); michael@0: } michael@0: return j; michael@0: } michael@0: michael@0: public: michael@0: void nop() { masm.nop(); } michael@0: void j(Condition cond, Label *label) { jSrc(cond, label); } michael@0: void jmp(Label *label) { jmpSrc(label); } michael@0: void j(Condition cond, RepatchLabel *label) { jSrc(cond, label); } michael@0: void jmp(RepatchLabel *label) { jmpSrc(label); } michael@0: michael@0: void jmp(const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.jmp_m(op.disp(), op.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.jmp_m(op.disp(), op.base(), op.index(), op.scale()); michael@0: break; michael@0: case Operand::REG: michael@0: masm.jmp_r(op.reg()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cmpEAX(Label *label) { cmpSrc(label); } michael@0: void bind(Label *label) { michael@0: JSC::MacroAssembler::Label jsclabel; michael@0: JSC::X86Assembler::JmpDst dst(masm.label()); michael@0: if (label->used()) { michael@0: bool more; michael@0: JSC::X86Assembler::JmpSrc jmp(label->offset()); michael@0: do { michael@0: JSC::X86Assembler::JmpSrc next; michael@0: more = masm.nextJump(jmp, &next); michael@0: masm.linkJump(jmp, dst); michael@0: jmp = next; michael@0: } while (more); michael@0: } michael@0: label->bind(dst.offset()); michael@0: } michael@0: void bind(RepatchLabel *label) { michael@0: JSC::MacroAssembler::Label jsclabel; michael@0: JSC::X86Assembler::JmpDst dst(masm.label()); michael@0: if (label->used()) { michael@0: JSC::X86Assembler::JmpSrc jmp(label->offset()); michael@0: masm.linkJump(jmp, dst); michael@0: } michael@0: label->bind(dst.offset()); michael@0: } michael@0: uint32_t currentOffset() { michael@0: return masm.label().offset(); michael@0: } michael@0: michael@0: // Re-routes pending jumps to a new label. michael@0: void retarget(Label *label, Label *target) { michael@0: JSC::MacroAssembler::Label jsclabel; michael@0: if (label->used()) { michael@0: bool more; michael@0: JSC::X86Assembler::JmpSrc jmp(label->offset()); michael@0: do { michael@0: JSC::X86Assembler::JmpSrc next; michael@0: more = masm.nextJump(jmp, &next); michael@0: michael@0: if (target->bound()) { michael@0: // The jump can be immediately patched to the correct destination. michael@0: masm.linkJump(jmp, JmpDst(target->offset())); michael@0: } else { michael@0: // Thread the jump list through the unpatched jump targets. michael@0: JmpSrc prev = JmpSrc(target->use(jmp.offset())); michael@0: masm.setNextJump(jmp, prev); michael@0: } michael@0: michael@0: jmp = next; michael@0: } while (more); michael@0: } michael@0: label->reset(); michael@0: } michael@0: michael@0: static void Bind(uint8_t *raw, AbsoluteLabel *label, const void *address) { michael@0: if (label->used()) { michael@0: intptr_t src = label->offset(); michael@0: do { michael@0: intptr_t next = reinterpret_cast(JSC::X86Assembler::getPointer(raw + src)); michael@0: JSC::X86Assembler::setPointer(raw + src, address); michael@0: src = next; michael@0: } while (src != AbsoluteLabel::INVALID_OFFSET); michael@0: } michael@0: label->bind(); michael@0: } michael@0: michael@0: // See Bind and JSC::X86Assembler::setPointer. michael@0: size_t labelOffsetToPatchOffset(size_t offset) { michael@0: return offset - sizeof(void*); michael@0: } michael@0: michael@0: void ret() { michael@0: masm.ret(); michael@0: } michael@0: void retn(Imm32 n) { michael@0: // Remove the size of the return address which is included in the frame. michael@0: masm.ret(n.value - sizeof(void *)); michael@0: } michael@0: void call(Label *label) { michael@0: if (label->bound()) { michael@0: masm.linkJump(masm.call(), JmpDst(label->offset())); michael@0: } else { michael@0: JmpSrc j = masm.call(); michael@0: JmpSrc prev = JmpSrc(label->use(j.offset())); michael@0: masm.setNextJump(j, prev); michael@0: } michael@0: } michael@0: void call(const Register ®) { michael@0: masm.call(reg.code()); michael@0: } michael@0: void call(const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.call(op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.call_m(op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: michael@0: void breakpoint() { michael@0: masm.int3(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static bool HasSSE2() { michael@0: return JSC::MacroAssembler::isSSE2Present(); michael@0: } michael@0: #endif michael@0: static bool HasSSE3() { michael@0: return JSC::MacroAssembler::isSSE3Present(); michael@0: } michael@0: static bool HasSSE41() { michael@0: return JSC::MacroAssembler::isSSE41Present(); michael@0: } michael@0: michael@0: // The below cmpl methods switch the lhs and rhs when it invokes the michael@0: // macroassembler to conform with intel standard. When calling this michael@0: // function put the left operand on the left as you would expect. michael@0: void cmpl(const Register &lhs, const Register &rhs) { michael@0: masm.cmpl_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void cmpl(const Register &lhs, const Operand &rhs) { michael@0: switch (rhs.kind()) { michael@0: case Operand::REG: michael@0: masm.cmpl_rr(rhs.reg(), lhs.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cmpl_mr(rhs.disp(), rhs.base(), lhs.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cmpl(const Register &src, Imm32 imm) { michael@0: masm.cmpl_ir(imm.value, src.code()); michael@0: } michael@0: void cmpl(const Operand &op, Imm32 imm) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.cmpl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cmpl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.cmpl_im(imm.value, op.disp(), op.base(), op.index(), op.scale()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.cmpl_im(imm.value, op.address()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cmpl(const Operand &lhs, const Register &rhs) { michael@0: switch (lhs.kind()) { michael@0: case Operand::REG: michael@0: masm.cmpl_rr(rhs.code(), lhs.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cmpl_rm(rhs.code(), lhs.disp(), lhs.base()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.cmpl_rm(rhs.code(), lhs.address()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cmpl(const Operand &op, ImmWord imm) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.cmpl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cmpl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.cmpl_im(imm.value, op.address()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cmpl(const Operand &op, ImmPtr imm) { michael@0: cmpl(op, ImmWord(uintptr_t(imm.value))); michael@0: } michael@0: void cmpw(const Register &lhs, const Register &rhs) { michael@0: masm.cmpw_rr(lhs.code(), rhs.code()); michael@0: } michael@0: void setCC(Condition cond, const Register &r) { michael@0: masm.setCC_r(static_cast(cond), r.code()); michael@0: } michael@0: void testb(const Register &lhs, const Register &rhs) { michael@0: JS_ASSERT(GeneralRegisterSet(Registers::SingleByteRegs).has(lhs)); michael@0: JS_ASSERT(GeneralRegisterSet(Registers::SingleByteRegs).has(rhs)); michael@0: masm.testb_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void testw(const Register &lhs, const Register &rhs) { michael@0: masm.testw_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void testl(const Register &lhs, const Register &rhs) { michael@0: masm.testl_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void testl(const Register &lhs, Imm32 rhs) { michael@0: masm.testl_i32r(rhs.value, lhs.code()); michael@0: } michael@0: void testl(const Operand &lhs, Imm32 rhs) { michael@0: switch (lhs.kind()) { michael@0: case Operand::REG: michael@0: masm.testl_i32r(rhs.value, lhs.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.testl_i32m(rhs.value, lhs.disp(), lhs.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void addl(Imm32 imm, const Register &dest) { michael@0: masm.addl_ir(imm.value, dest.code()); michael@0: } michael@0: void addl(Imm32 imm, const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.addl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.addl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.addl_im(imm.value, op.address()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void subl(Imm32 imm, const Register &dest) { michael@0: masm.subl_ir(imm.value, dest.code()); michael@0: } michael@0: void subl(Imm32 imm, const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.subl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.subl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void addl(const Register &src, const Register &dest) { michael@0: masm.addl_rr(src.code(), dest.code()); michael@0: } michael@0: void subl(const Register &src, const Register &dest) { michael@0: masm.subl_rr(src.code(), dest.code()); michael@0: } michael@0: void subl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.subl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.subl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void subl(const Register &src, const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::REG: michael@0: masm.subl_rr(src.code(), dest.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.subl_rm(src.code(), dest.disp(), dest.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void orl(const Register ®, const Register &dest) { michael@0: masm.orl_rr(reg.code(), dest.code()); michael@0: } michael@0: void orl(Imm32 imm, const Register ®) { michael@0: masm.orl_ir(imm.value, reg.code()); michael@0: } michael@0: void orl(Imm32 imm, const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.orl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.orl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void xorl(const Register &src, const Register &dest) { michael@0: masm.xorl_rr(src.code(), dest.code()); michael@0: } michael@0: void xorl(Imm32 imm, const Register ®) { michael@0: masm.xorl_ir(imm.value, reg.code()); michael@0: } michael@0: void xorl(Imm32 imm, const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.xorl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.xorl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void andl(const Register &src, const Register &dest) { michael@0: masm.andl_rr(src.code(), dest.code()); michael@0: } michael@0: void andl(Imm32 imm, const Register &dest) { michael@0: masm.andl_ir(imm.value, dest.code()); michael@0: } michael@0: void andl(Imm32 imm, const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::REG: michael@0: masm.andl_ir(imm.value, op.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.andl_im(imm.value, op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void addl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.addl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.addl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void orl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.orl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.orl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void xorl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.xorl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.xorl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void andl(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.andl_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.andl_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void imull(const Register &multiplier) { michael@0: masm.imull_r(multiplier.code()); michael@0: } michael@0: void imull(Imm32 imm, const Register &dest) { michael@0: masm.imull_i32r(dest.code(), imm.value, dest.code()); michael@0: } michael@0: void imull(const Register &src, const Register &dest) { michael@0: masm.imull_rr(src.code(), dest.code()); michael@0: } michael@0: void imull(Imm32 imm, const Register &src, const Register &dest) { michael@0: masm.imull_i32r(src.code(), imm.value, dest.code()); michael@0: } michael@0: void imull(const Operand &src, const Register &dest) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.imull_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.imull_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void negl(const Operand &src) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.negl_r(src.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.negl_m(src.disp(), src.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void negl(const Register ®) { michael@0: masm.negl_r(reg.code()); michael@0: } michael@0: void notl(const Operand &src) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.notl_r(src.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.notl_m(src.disp(), src.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void notl(const Register ®) { michael@0: masm.notl_r(reg.code()); michael@0: } michael@0: void shrl(const Imm32 imm, const Register &dest) { michael@0: masm.shrl_i8r(imm.value, dest.code()); michael@0: } michael@0: void shll(const Imm32 imm, const Register &dest) { michael@0: masm.shll_i8r(imm.value, dest.code()); michael@0: } michael@0: void sarl(const Imm32 imm, const Register &dest) { michael@0: masm.sarl_i8r(imm.value, dest.code()); michael@0: } michael@0: void shrl_cl(const Register &dest) { michael@0: masm.shrl_CLr(dest.code()); michael@0: } michael@0: void shll_cl(const Register &dest) { michael@0: masm.shll_CLr(dest.code()); michael@0: } michael@0: void sarl_cl(const Register &dest) { michael@0: masm.sarl_CLr(dest.code()); michael@0: } michael@0: michael@0: void incl(const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.incl_m32(op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void lock_incl(const Operand &op) { michael@0: masm.prefix_lock(); michael@0: incl(op); michael@0: } michael@0: michael@0: void decl(const Operand &op) { michael@0: switch (op.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.decl_m32(op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void lock_decl(const Operand &op) { michael@0: masm.prefix_lock(); michael@0: decl(op); michael@0: } michael@0: michael@0: void lock_cmpxchg32(const Register &src, const Operand &op) { michael@0: masm.prefix_lock(); michael@0: switch (op.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cmpxchg32(src.code(), op.disp(), op.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: michael@0: void xaddl(const Register &srcdest, const Operand &mem) { michael@0: switch (mem.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.xaddl_rm(srcdest.code(), mem.disp(), mem.base()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.xaddl_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: michael@0: void push(const Imm32 imm) { michael@0: masm.push_i32(imm.value); michael@0: } michael@0: michael@0: void push(const Operand &src) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.push_r(src.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.push_m(src.disp(), src.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void push(const Register &src) { michael@0: masm.push_r(src.code()); michael@0: } michael@0: void push(const Address &src) { michael@0: masm.push_m(src.offset, src.base.code()); michael@0: } michael@0: michael@0: void pop(const Operand &src) { michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.pop_r(src.reg()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.pop_m(src.disp(), src.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void pop(const Register &src) { michael@0: masm.pop_r(src.code()); michael@0: } michael@0: michael@0: void pushFlags() { michael@0: masm.push_flags(); michael@0: } michael@0: void popFlags() { michael@0: masm.pop_flags(); michael@0: } michael@0: michael@0: #ifdef JS_CODEGEN_X86 michael@0: void pushAllRegs() { michael@0: masm.pusha(); michael@0: } michael@0: void popAllRegs() { michael@0: masm.popa(); michael@0: } michael@0: #endif michael@0: michael@0: // Zero-extend byte to 32-bit integer. michael@0: void movzbl(const Register &src, const Register &dest) { michael@0: masm.movzbl_rr(src.code(), dest.code()); michael@0: } michael@0: michael@0: void cdq() { michael@0: masm.cdq(); michael@0: } michael@0: void idiv(Register divisor) { michael@0: masm.idivl_r(divisor.code()); michael@0: } michael@0: void udiv(Register divisor) { michael@0: masm.divl_r(divisor.code()); michael@0: } michael@0: michael@0: void unpcklps(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.unpcklps_rr(src.code(), dest.code()); michael@0: } michael@0: void pinsrd(const Register &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.pinsrd_rr(src.code(), dest.code()); michael@0: } michael@0: void pinsrd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.pinsrd_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.pinsrd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void psrldq(Imm32 shift, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.psrldq_ir(shift.value, dest.code()); michael@0: } michael@0: void psllq(Imm32 shift, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.psllq_ir(shift.value, dest.code()); michael@0: } michael@0: void psrlq(Imm32 shift, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.psrlq_ir(shift.value, dest.code()); michael@0: } michael@0: michael@0: void cvtsi2sd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.cvtsi2sd_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cvtsi2sd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.cvtsi2sd_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cvttsd2si(const FloatRegister &src, const Register &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.cvttsd2si_rr(src.code(), dest.code()); michael@0: } michael@0: void cvttss2si(const FloatRegister &src, const Register &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.cvttss2si_rr(src.code(), dest.code()); michael@0: } michael@0: void cvtsi2ss(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::REG: michael@0: masm.cvtsi2ss_rr(src.reg(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.cvtsi2ss_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_SCALE: michael@0: masm.cvtsi2ss_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void cvtsi2ss(const Register &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.cvtsi2ss_rr(src.code(), dest.code()); michael@0: } michael@0: void cvtsi2sd(const Register &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.cvtsi2sd_rr(src.code(), dest.code()); michael@0: } michael@0: void movmskpd(const FloatRegister &src, const Register &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.movmskpd_rr(src.code(), dest.code()); michael@0: } michael@0: void movmskps(const FloatRegister &src, const Register &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.movmskps_rr(src.code(), dest.code()); michael@0: } michael@0: void ptest(const FloatRegister &lhs, const FloatRegister &rhs) { michael@0: JS_ASSERT(HasSSE41()); michael@0: masm.ptest_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void ucomisd(const FloatRegister &lhs, const FloatRegister &rhs) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.ucomisd_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void ucomiss(const FloatRegister &lhs, const FloatRegister &rhs) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.ucomiss_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void pcmpeqw(const FloatRegister &lhs, const FloatRegister &rhs) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.pcmpeqw_rr(rhs.code(), lhs.code()); michael@0: } michael@0: void movd(const Register &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.movd_rr(src.code(), dest.code()); michael@0: } michael@0: void movd(const FloatRegister &src, const Register &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.movd_rr(src.code(), dest.code()); michael@0: } michael@0: void addsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.addsd_rr(src.code(), dest.code()); michael@0: } michael@0: void addss(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.addss_rr(src.code(), dest.code()); michael@0: } michael@0: void addsd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.addsd_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.addsd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.addsd_mr(src.address(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void addss(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.addss_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.addss_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: case Operand::MEM_ADDRESS32: michael@0: masm.addss_mr(src.address(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void subsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.subsd_rr(src.code(), dest.code()); michael@0: } michael@0: void subss(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.subss_rr(src.code(), dest.code()); michael@0: } michael@0: void subsd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.subsd_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.subsd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void subss(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.subss_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.subss_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void mulsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.mulsd_rr(src.code(), dest.code()); michael@0: } michael@0: void mulsd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.mulsd_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.mulsd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void mulss(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.mulss_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.mulss_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void mulss(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.mulss_rr(src.code(), dest.code()); michael@0: } michael@0: void divsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.divsd_rr(src.code(), dest.code()); michael@0: } michael@0: void divss(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.divss_rr(src.code(), dest.code()); michael@0: } michael@0: void divsd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.divsd_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.divsd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void divss(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.divss_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.divss_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void xorpd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.xorpd_rr(src.code(), dest.code()); michael@0: } michael@0: void xorps(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.xorps_rr(src.code(), dest.code()); michael@0: } michael@0: void orpd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.orpd_rr(src.code(), dest.code()); michael@0: } michael@0: void andpd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.andpd_rr(src.code(), dest.code()); michael@0: } michael@0: void andps(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.andps_rr(src.code(), dest.code()); michael@0: } michael@0: void sqrtsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.sqrtsd_rr(src.code(), dest.code()); michael@0: } michael@0: void sqrtss(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.sqrtss_rr(src.code(), dest.code()); michael@0: } michael@0: void roundsd(const FloatRegister &src, const FloatRegister &dest, michael@0: JSC::X86Assembler::RoundingMode mode) michael@0: { michael@0: JS_ASSERT(HasSSE41()); michael@0: masm.roundsd_rr(src.code(), dest.code(), mode); michael@0: } michael@0: void roundss(const FloatRegister &src, const FloatRegister &dest, michael@0: JSC::X86Assembler::RoundingMode mode) michael@0: { michael@0: JS_ASSERT(HasSSE41()); michael@0: masm.roundss_rr(src.code(), dest.code(), mode); michael@0: } michael@0: void minsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.minsd_rr(src.code(), dest.code()); michael@0: } michael@0: void minsd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.minsd_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.minsd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void maxsd(const FloatRegister &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: masm.maxsd_rr(src.code(), dest.code()); michael@0: } michael@0: void maxsd(const Operand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(HasSSE2()); michael@0: switch (src.kind()) { michael@0: case Operand::FPREG: michael@0: masm.maxsd_rr(src.fpu(), dest.code()); michael@0: break; michael@0: case Operand::MEM_REG_DISP: michael@0: masm.maxsd_mr(src.disp(), src.base(), dest.code()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void fisttp(const Operand &dest) { michael@0: JS_ASSERT(HasSSE3()); michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.fisttp_m(dest.disp(), dest.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void fld(const Operand &dest) { michael@0: switch (dest.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.fld_m(dest.disp(), dest.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void fstp(const Operand &src) { michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.fstp_m(src.disp(), src.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: void fstp32(const Operand &src) { michael@0: switch (src.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: masm.fstp32_m(src.disp(), src.base()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: michael@0: // Defined for compatibility with ARM's assembler michael@0: uint32_t actualOffset(uint32_t x) { michael@0: return x; michael@0: } michael@0: michael@0: uint32_t actualIndex(uint32_t x) { michael@0: return x; michael@0: } michael@0: michael@0: void flushBuffer() { michael@0: } michael@0: michael@0: // Patching. michael@0: michael@0: static size_t patchWrite_NearCallSize() { michael@0: return 5; michael@0: } michael@0: static uintptr_t getPointer(uint8_t *instPtr) { michael@0: uintptr_t *ptr = ((uintptr_t *) instPtr) - 1; michael@0: return *ptr; michael@0: } michael@0: // Write a relative call at the start location |dataLabel|. michael@0: // Note that this DOES NOT patch data that comes before |label|. michael@0: static void patchWrite_NearCall(CodeLocationLabel startLabel, CodeLocationLabel target) { michael@0: uint8_t *start = startLabel.raw(); michael@0: *start = 0xE8; michael@0: ptrdiff_t offset = target - startLabel - patchWrite_NearCallSize(); michael@0: JS_ASSERT(int32_t(offset) == offset); michael@0: *((int32_t *) (start + 1)) = offset; michael@0: } michael@0: michael@0: static void patchWrite_Imm32(CodeLocationLabel dataLabel, Imm32 toWrite) { michael@0: *((int32_t *) dataLabel.raw() - 1) = toWrite.value; michael@0: } michael@0: michael@0: static void patchDataWithValueCheck(CodeLocationLabel data, PatchedImmPtr newData, michael@0: PatchedImmPtr expectedData) { michael@0: // The pointer given is a pointer to *after* the data. michael@0: uintptr_t *ptr = ((uintptr_t *) data.raw()) - 1; michael@0: JS_ASSERT(*ptr == (uintptr_t)expectedData.value); michael@0: *ptr = (uintptr_t)newData.value; michael@0: } michael@0: static void patchDataWithValueCheck(CodeLocationLabel data, ImmPtr newData, ImmPtr expectedData) { michael@0: patchDataWithValueCheck(data, PatchedImmPtr(newData.value), PatchedImmPtr(expectedData.value)); michael@0: } michael@0: static uint32_t nopSize() { michael@0: return 1; michael@0: } michael@0: static uint8_t *nextInstruction(uint8_t *cur, uint32_t *count) { michael@0: MOZ_ASSUME_UNREACHABLE("nextInstruction NYI on x86"); michael@0: } michael@0: michael@0: // Toggle a jmp or cmp emitted by toggledJump(). michael@0: static void ToggleToJmp(CodeLocationLabel inst) { michael@0: uint8_t *ptr = (uint8_t *)inst.raw(); michael@0: JS_ASSERT(*ptr == 0x3D); michael@0: *ptr = 0xE9; michael@0: } michael@0: static void ToggleToCmp(CodeLocationLabel inst) { michael@0: uint8_t *ptr = (uint8_t *)inst.raw(); michael@0: JS_ASSERT(*ptr == 0xE9); michael@0: *ptr = 0x3D; michael@0: } michael@0: static void ToggleCall(CodeLocationLabel inst, bool enabled) { michael@0: uint8_t *ptr = (uint8_t *)inst.raw(); michael@0: JS_ASSERT(*ptr == 0x3D || // CMP michael@0: *ptr == 0xE8); // CALL michael@0: *ptr = enabled ? 0xE8 : 0x3D; michael@0: } michael@0: }; michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_shared_Assembler_x86_shared_h */