| /* -*- 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_Architecture_x86_h |
| #define jit_x86_shared_Architecture_x86_h |
| |
| #if !defined(JS_CODEGEN_X86) && !defined(JS_CODEGEN_X64) |
| # error "Unsupported architecture!" |
| #endif |
| |
| #include "mozilla/MathAlgorithms.h" |
| |
| #include <string.h> |
| |
| #include "jit/x86-shared/Constants-x86-shared.h" |
| |
| namespace js { |
| namespace jit { |
| |
| #if defined(JS_CODEGEN_X86) |
| // In bytes: slots needed for potential memory->memory move spills. |
| // +8 for cycles |
| // +4 for gpr spills |
| // +8 for double spills |
| static const uint32_t ION_FRAME_SLACK_SIZE = 20; |
| |
| #elif defined(JS_CODEGEN_X64) |
| // In bytes: slots needed for potential memory->memory move spills. |
| // +8 for cycles |
| // +8 for gpr spills |
| // +8 for double spills |
| static const uint32_t ION_FRAME_SLACK_SIZE = 24; |
| #endif |
| |
| #if defined(JS_CODEGEN_X86) |
| // These offsets are specific to nunboxing, and capture offsets into the |
| // components of a js::Value. |
| static const int32_t NUNBOX32_TYPE_OFFSET = 4; |
| static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0; |
| |
| // Size of each bailout table entry. On x86 this is a 5-byte relative call. |
| static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = 5; |
| #endif |
| |
| #if defined(JS_CODEGEN_X64) && defined(_WIN64) |
| static const uint32_t ShadowStackSpace = 32; |
| #else |
| static const uint32_t ShadowStackSpace = 0; |
| #endif |
| |
| class Registers { |
| public: |
| typedef uint8_t Code; |
| typedef X86Encoding::RegisterID Encoding; |
| |
| // Content spilled during bailouts. |
| union RegisterContent { |
| uintptr_t r; |
| }; |
| |
| #if defined(JS_CODEGEN_X86) |
| typedef uint8_t SetType; |
| |
| static const char* GetName(Code code) { |
| return X86Encoding::GPRegName(Encoding(code)); |
| } |
| |
| static const uint32_t Total = 8; |
| static const uint32_t TotalPhys = 8; |
| static const uint32_t Allocatable = 7; |
| |
| #elif defined(JS_CODEGEN_X64) |
| typedef uint16_t SetType; |
| |
| static const char* GetName(Code code) { |
| static const char * const Names[] = { "rax", "rcx", "rdx", "rbx", |
| "rsp", "rbp", "rsi", "rdi", |
| "r8", "r9", "r10", "r11", |
| "r12", "r13", "r14", "r15" }; |
| return Names[code]; |
| } |
| |
| static const uint32_t Total = 16; |
| static const uint32_t TotalPhys = 16; |
| static const uint32_t Allocatable = 14; |
| #endif |
| |
| static uint32_t SetSize(SetType x) { |
| static_assert(sizeof(SetType) <= 4, "SetType must be, at most, 32 bits"); |
| return mozilla::CountPopulation32(x); |
| } |
| static uint32_t FirstBit(SetType x) { |
| return mozilla::CountTrailingZeroes32(x); |
| } |
| static uint32_t LastBit(SetType x) { |
| return 31 - mozilla::CountLeadingZeroes32(x); |
| } |
| |
| static Code FromName(const char* name) { |
| for (size_t i = 0; i < Total; i++) { |
| if (strcmp(GetName(Code(i)), name) == 0) |
| return Code(i); |
| } |
| return Invalid; |
| } |
| |
| static const Encoding StackPointer = X86Encoding::rsp; |
| static const Encoding Invalid = X86Encoding::invalid_reg; |
| |
| static const SetType AllMask = (1 << Total) - 1; |
| |
| #if defined(JS_CODEGEN_X86) |
| static const SetType ArgRegMask = 0; |
| |
| static const SetType VolatileMask = |
| (1 << X86Encoding::rax) | |
| (1 << X86Encoding::rcx) | |
| (1 << X86Encoding::rdx); |
| |
| static const SetType WrapperMask = |
| VolatileMask | |
| (1 << X86Encoding::rbx); |
| |
| static const SetType SingleByteRegs = |
| (1 << X86Encoding::rax) | |
| (1 << X86Encoding::rcx) | |
| (1 << X86Encoding::rdx) | |
| (1 << X86Encoding::rbx); |
| |
| static const SetType NonAllocatableMask = |
| (1 << X86Encoding::rsp); |
| |
| // Registers returned from a JS -> JS call. |
| static const SetType JSCallMask = |
| (1 << X86Encoding::rcx) | |
| (1 << X86Encoding::rdx); |
| |
| // Registers returned from a JS -> C call. |
| static const SetType CallMask = |
| (1 << X86Encoding::rax); |
| |
| #elif defined(JS_CODEGEN_X64) |
| static const SetType ArgRegMask = |
| # if !defined(_WIN64) |
| (1 << X86Encoding::rdi) | |
| (1 << X86Encoding::rsi) | |
| # endif |
| (1 << X86Encoding::rdx) | |
| (1 << X86Encoding::rcx) | |
| (1 << X86Encoding::r8) | |
| (1 << X86Encoding::r9); |
| |
| static const SetType VolatileMask = |
| (1 << X86Encoding::rax) | |
| ArgRegMask | |
| (1 << X86Encoding::r10) | |
| (1 << X86Encoding::r11); |
| |
| static const SetType WrapperMask = VolatileMask; |
| |
| static const SetType SingleByteRegs = AllMask & ~(1 << X86Encoding::rsp); |
| |
| static const SetType NonAllocatableMask = |
| (1 << X86Encoding::rsp) | |
| (1 << X86Encoding::r11); // This is ScratchReg. |
| |
| // Registers returned from a JS -> JS call. |
| static const SetType JSCallMask = |
| (1 << X86Encoding::rcx); |
| |
| // Registers returned from a JS -> C call. |
| static const SetType CallMask = |
| (1 << X86Encoding::rax); |
| |
| #endif |
| |
| static const SetType NonVolatileMask = |
| AllMask & ~VolatileMask & ~(1 << X86Encoding::rsp); |
| |
| static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; |
| |
| // Registers that can be allocated without being saved, generally. |
| static const SetType TempMask = VolatileMask & ~NonAllocatableMask; |
| }; |
| |
| typedef Registers::SetType PackedRegisterMask; |
| |
| class FloatRegisters { |
| public: |
| typedef X86Encoding::XMMRegisterID Encoding; |
| |
| enum ContentType { |
| Single, // 32-bit float. |
| Double, // 64-bit double. |
| Simd128, // 128-bit SIMD type (int32x4, bool16x8, etc). |
| NumTypes |
| }; |
| |
| // Content spilled during bailouts. |
| union RegisterContent { |
| float s; |
| double d; |
| int32_t i4[4]; |
| float s4[4]; |
| }; |
| |
| static const char* GetName(Encoding code) { |
| return X86Encoding::XMMRegName(code); |
| } |
| |
| static Encoding FromName(const char* name) { |
| for (size_t i = 0; i < Total; i++) { |
| if (strcmp(GetName(Encoding(i)), name) == 0) |
| return Encoding(i); |
| } |
| return Invalid; |
| } |
| |
| static const Encoding Invalid = X86Encoding::invalid_xmm; |
| |
| #if defined(JS_CODEGEN_X86) |
| static const uint32_t Total = 8 * NumTypes; |
| static const uint32_t TotalPhys = 8; |
| static const uint32_t Allocatable = 7; |
| typedef uint32_t SetType; |
| |
| #elif defined(JS_CODEGEN_X64) |
| static const uint32_t Total = 16 * NumTypes; |
| static const uint32_t TotalPhys = 16; |
| static const uint32_t Allocatable = 15; |
| typedef uint64_t SetType; |
| |
| #endif |
| |
| static_assert(sizeof(SetType) * 8 >= Total, |
| "SetType should be large enough to enumerate all registers."); |
| |
| // Magic values which are used to duplicate a mask of physical register for |
| // a specific type of register. A multiplication is used to copy and shift |
| // the bits of the physical register mask. |
| static const SetType SpreadSingle = SetType(1) << (uint32_t(Single) * TotalPhys); |
| static const SetType SpreadDouble = SetType(1) << (uint32_t(Double) * TotalPhys); |
| static const SetType SpreadSimd128 = SetType(1) << (uint32_t(Simd128) * TotalPhys); |
| static const SetType SpreadScalar = SpreadSingle | SpreadDouble; |
| static const SetType SpreadVector = SpreadSimd128; |
| static const SetType Spread = SpreadScalar | SpreadVector; |
| |
| static const SetType AllPhysMask = ((1 << TotalPhys) - 1); |
| static const SetType AllMask = AllPhysMask * Spread; |
| static const SetType AllDoubleMask = AllPhysMask * SpreadDouble; |
| |
| #if defined(JS_CODEGEN_X86) |
| static const SetType NonAllocatableMask = |
| Spread * (1 << X86Encoding::xmm7); // This is ScratchDoubleReg. |
| |
| #elif defined(JS_CODEGEN_X64) |
| static const SetType NonAllocatableMask = |
| Spread * (1 << X86Encoding::xmm15); // This is ScratchDoubleReg. |
| #endif |
| |
| #if defined(JS_CODEGEN_X64) && defined(_WIN64) |
| static const SetType VolatileMask = |
| ( (1 << X86Encoding::xmm0) | |
| (1 << X86Encoding::xmm1) | |
| (1 << X86Encoding::xmm2) | |
| (1 << X86Encoding::xmm3) | |
| (1 << X86Encoding::xmm4) | |
| (1 << X86Encoding::xmm5) |
| ) * SpreadScalar |
| | AllPhysMask * SpreadVector; |
| |
| #else |
| static const SetType VolatileMask = |
| AllMask; |
| #endif |
| |
| static const SetType NonVolatileMask = AllMask & ~VolatileMask; |
| static const SetType WrapperMask = VolatileMask; |
| static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; |
| }; |
| |
| template <typename T> |
| class TypedRegisterSet; |
| |
| struct FloatRegister { |
| typedef FloatRegisters Codes; |
| typedef size_t Code; |
| typedef Codes::Encoding Encoding; |
| typedef Codes::SetType SetType; |
| static uint32_t SetSize(SetType x) { |
| // Count the number of non-aliased registers, for the moment. |
| // |
| // Copy the set bits of each typed register to the low part of the of |
| // the Set, and count the number of registers. This is made to avoid |
| // registers which are allocated twice with different types (such as in |
| // AllMask). |
| x |= x >> (2 * Codes::TotalPhys); |
| x |= x >> Codes::TotalPhys; |
| x &= Codes::AllPhysMask; |
| static_assert(Codes::AllPhysMask <= 0xffff, "We can safely use CountPopulation32"); |
| return mozilla::CountPopulation32(x); |
| } |
| |
| #if defined(JS_CODEGEN_X86) |
| static uint32_t FirstBit(SetType x) { |
| static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); |
| return mozilla::CountTrailingZeroes32(x); |
| } |
| static uint32_t LastBit(SetType x) { |
| return 31 - mozilla::CountLeadingZeroes32(x); |
| } |
| |
| #elif defined(JS_CODEGEN_X64) |
| static uint32_t FirstBit(SetType x) { |
| static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); |
| return mozilla::CountTrailingZeroes64(x); |
| } |
| static uint32_t LastBit(SetType x) { |
| return 63 - mozilla::CountLeadingZeroes64(x); |
| } |
| #endif |
| |
| private: |
| // Note: These fields are using one extra bit to make the invalid enumerated |
| // values fit, and thus prevent a warning. |
| Codes::Encoding reg_ : 5; |
| Codes::ContentType type_ : 3; |
| bool isInvalid_ : 1; |
| |
| // Constants used for exporting/importing the float register code. |
| #if defined(JS_CODEGEN_X86) |
| static const size_t RegSize = 3; |
| #elif defined(JS_CODEGEN_X64) |
| static const size_t RegSize = 4; |
| #endif |
| static const size_t RegMask = (1 << RegSize) - 1; |
| |
| public: |
| MOZ_CONSTEXPR FloatRegister() |
| : reg_(Codes::Encoding(0)), type_(Codes::Single), isInvalid_(true) |
| { } |
| MOZ_CONSTEXPR FloatRegister(uint32_t r, Codes::ContentType k) |
| : reg_(Codes::Encoding(r)), type_(k), isInvalid_(false) |
| { } |
| MOZ_CONSTEXPR FloatRegister(Codes::Encoding r, Codes::ContentType k) |
| : reg_(r), type_(k), isInvalid_(false) |
| { } |
| |
| static FloatRegister FromCode(uint32_t i) { |
| MOZ_ASSERT(i < Codes::Total); |
| return FloatRegister(i & RegMask, Codes::ContentType(i >> RegSize)); |
| } |
| |
| bool isSingle() const { MOZ_ASSERT(!isInvalid()); return type_ == Codes::Single; } |
| bool isDouble() const { MOZ_ASSERT(!isInvalid()); return type_ == Codes::Double; } |
| bool isSimd128() const { MOZ_ASSERT(!isInvalid()); return type_ == Codes::Simd128; } |
| bool isInvalid() const { return isInvalid_; } |
| |
| FloatRegister asSingle() const { MOZ_ASSERT(!isInvalid()); return FloatRegister(reg_, Codes::Single); } |
| FloatRegister asDouble() const { MOZ_ASSERT(!isInvalid()); return FloatRegister(reg_, Codes::Double); } |
| FloatRegister asSimd128() const { MOZ_ASSERT(!isInvalid()); return FloatRegister(reg_, Codes::Simd128); } |
| |
| uint32_t size() const { |
| MOZ_ASSERT(!isInvalid()); |
| if (isSingle()) |
| return sizeof(float); |
| if (isDouble()) |
| return sizeof(double); |
| MOZ_ASSERT(isSimd128()); |
| return 4 * sizeof(int32_t); |
| } |
| |
| Code code() const { |
| MOZ_ASSERT(!isInvalid()); |
| MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys); |
| // :TODO: ARM is doing the same thing, but we should avoid this, except |
| // that the RegisterSets depends on this. |
| return Code(reg_ | (type_ << RegSize)); |
| } |
| Encoding encoding() const { |
| MOZ_ASSERT(!isInvalid()); |
| MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys); |
| return reg_; |
| } |
| // defined in Assembler-x86-shared.cpp |
| const char* name() const; |
| bool volatile_() const { |
| return !!((SetType(1) << code()) & FloatRegisters::VolatileMask); |
| } |
| bool operator !=(FloatRegister other) const { |
| return other.reg_ != reg_ || other.type_ != type_; |
| } |
| bool operator ==(FloatRegister other) const { |
| return other.reg_ == reg_ && other.type_ == type_; |
| } |
| bool aliases(FloatRegister other) const { |
| return other.reg_ == reg_; |
| } |
| // Check if two floating point registers have the same type. |
| bool equiv(FloatRegister other) const { |
| return other.type_ == type_; |
| } |
| |
| uint32_t numAliased() const { |
| return Codes::NumTypes; |
| } |
| uint32_t numAlignedAliased() const { |
| return numAliased(); |
| } |
| |
| // N.B. FloatRegister is an explicit outparam here because msvc-2010 |
| // miscompiled it on win64 when the value was simply returned |
| void aliased(uint32_t aliasIdx, FloatRegister* ret) const { |
| MOZ_ASSERT(aliasIdx < Codes::NumTypes); |
| *ret = FloatRegister(reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes)); |
| } |
| void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) const { |
| aliased(aliasIdx, ret); |
| } |
| |
| SetType alignedOrDominatedAliasedSet() const { |
| return Codes::Spread << reg_; |
| } |
| |
| static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister>& s); |
| static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s); |
| uint32_t getRegisterDumpOffsetInBytes(); |
| }; |
| |
| // Arm/D32 has double registers that can NOT be treated as float32 |
| // and this requires some dances in lowering. |
| inline bool |
| hasUnaliasedDouble() |
| { |
| return false; |
| } |
| |
| // On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32 |
| // to a double as a temporary, you need a temporary double register. |
| inline bool |
| hasMultiAlias() |
| { |
| return false; |
| } |
| |
| // Support some constant-offset addressing. |
| // See the comments above AsmJSMappedSize in AsmJSValidate.h for more info. |
| static const size_t AsmJSCheckedImmediateRange = 4096; |
| static const size_t AsmJSImmediateRange = UINT32_C(0x80000000); |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* jit_x86_shared_Architecture_x86_h */ |