blob: 89e82f4cbcb437f0404c19425f53ae4078da79a1 [file] [log] [blame]
/* -*- 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 */