blob: b486dc66c8e84299704f2a1b637dcede11f25e39 [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_arm64_Architecture_arm64_h
#define jit_arm64_Architecture_arm64_h
#include "mozilla/Assertions.h"
#include "mozilla/MathAlgorithms.h"
#include "js/Utility.h"
namespace js {
namespace jit {
// AArch64 has 32 64-bit integer registers, x0 though x31.
// x31 is special and functions as both the stack pointer and a zero register.
// The bottom 32 bits of each of the X registers is accessible as w0 through w31.
// The program counter is no longer accessible as a register.
// SIMD and scalar floating-point registers share a register bank.
// 32 bit float registers are s0 through s31.
// 64 bit double registers are d0 through d31.
// 128 bit SIMD registers are v0 through v31.
// e.g., s0 is the bottom 32 bits of d0, which is the bottom 64 bits of v0.
// AArch64 Calling Convention:
// x0 - x7: arguments and return value
// x8: indirect result (struct) location
// x9 - x15: temporary registers
// x16 - x17: intra-call-use registers (PLT, linker)
// x18: platform specific use (TLS)
// x19 - x28: callee-saved registers
// x29: frame pointer
// x30: link register
// AArch64 Calling Convention for Floats:
// d0 - d7: arguments and return value
// d8 - d15: callee-saved registers
// Bits 64:128 are not saved for v8-v15.
// d16 - d31: temporary registers
// AArch64 does not have soft float.
class Registers {
public:
enum RegisterID {
w0 = 0, x0 = 0,
w1 = 1, x1 = 1,
w2 = 2, x2 = 2,
w3 = 3, x3 = 3,
w4 = 4, x4 = 4,
w5 = 5, x5 = 5,
w6 = 6, x6 = 6,
w7 = 7, x7 = 7,
w8 = 8, x8 = 8,
w9 = 9, x9 = 9,
w10 = 10, x10 = 10,
w11 = 11, x11 = 11,
w12 = 12, x12 = 12,
w13 = 13, x13 = 13,
w14 = 14, x14 = 14,
w15 = 15, x15 = 15,
w16 = 16, x16 = 16, ip0 = 16, // MacroAssembler scratch register 1.
w17 = 17, x17 = 17, ip1 = 17, // MacroAssembler scratch register 2.
w18 = 18, x18 = 18, tls = 18, // Platform-specific use (TLS).
w19 = 19, x19 = 19,
w20 = 20, x20 = 20,
w21 = 21, x21 = 21,
w22 = 22, x22 = 22,
w23 = 23, x23 = 23,
w24 = 24, x24 = 24,
w25 = 25, x25 = 25,
w26 = 26, x26 = 26,
w27 = 27, x27 = 27,
w28 = 28, x28 = 28,
w29 = 29, x29 = 29, fp = 29,
w30 = 30, x30 = 30, lr = 30,
w31 = 31, x31 = 31, wzr = 31, xzr = 31, sp = 31, // Special: both stack pointer and a zero register.
invalid_reg
};
typedef uint8_t Code;
typedef uint32_t Encoding;
typedef uint32_t SetType;
union RegisterContent {
uintptr_t r;
};
static uint32_t SetSize(SetType x) {
static_assert(sizeof(SetType) == 4, "SetType must be 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 const char* GetName(Code code) {
static const char* const Names[] =
{ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9",
"x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19",
"x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29",
"lr", "sp", "invalid" };
return Names[code];
}
static const char* GetName(uint32_t i) {
MOZ_ASSERT(i < Total);
return GetName(Code(i));
}
static Code FromName(const char* name);
// If SP is used as the base register for a memory load or store, then the value
// of the stack pointer prior to adding any offset must be quadword (16 byte) aligned,
// or else a stack aligment exception will be generated.
static const Code StackPointer = sp;
static const Code Invalid = invalid_reg;
static const uint32_t Total = 32;
static const uint32_t TotalPhys = 32;
static const uint32_t Allocatable = 27; // No named special-function registers.
static const SetType AllMask = 0xFFFFFFFF;
static const SetType ArgRegMask =
(1 << Registers::x0) | (1 << Registers::x1) |
(1 << Registers::x2) | (1 << Registers::x3) |
(1 << Registers::x4) | (1 << Registers::x5) |
(1 << Registers::x6) | (1 << Registers::x7) |
(1 << Registers::x8);
static const SetType VolatileMask =
(1 << Registers::x0) | (1 << Registers::x1) |
(1 << Registers::x2) | (1 << Registers::x3) |
(1 << Registers::x4) | (1 << Registers::x5) |
(1 << Registers::x6) | (1 << Registers::x7) |
(1 << Registers::x8) | (1 << Registers::x9) |
(1 << Registers::x10) | (1 << Registers::x11) |
(1 << Registers::x11) | (1 << Registers::x12) |
(1 << Registers::x13) | (1 << Registers::x14) |
(1 << Registers::x14) | (1 << Registers::x15) |
(1 << Registers::x16) | (1 << Registers::x17) |
(1 << Registers::x18);
static const SetType NonVolatileMask =
(1 << Registers::x19) | (1 << Registers::x20) |
(1 << Registers::x21) | (1 << Registers::x22) |
(1 << Registers::x23) | (1 << Registers::x24) |
(1 << Registers::x25) | (1 << Registers::x26) |
(1 << Registers::x27) | (1 << Registers::x28) |
(1 << Registers::x29) | (1 << Registers::x30);
static const SetType SingleByteRegs = VolatileMask | NonVolatileMask;
static const SetType NonAllocatableMask =
(1 << Registers::x28) | // PseudoStackPointer.
(1 << Registers::ip0) | // First scratch register.
(1 << Registers::ip1) | // Second scratch register.
(1 << Registers::tls) |
(1 << Registers::lr) |
(1 << Registers::sp);
// Registers that can be allocated without being saved, generally.
static const SetType TempMask = VolatileMask & ~NonAllocatableMask;
static const SetType WrapperMask = VolatileMask;
// Registers returned from a JS -> JS call.
static const SetType JSCallMask = (1 << Registers::x2);
// Registers returned from a JS -> C call.
static const SetType CallMask = (1 << Registers::x0);
static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
};
// Smallest integer type that can hold a register bitmask.
typedef uint32_t PackedRegisterMask;
template <typename T>
class TypedRegisterSet;
class FloatRegisters
{
public:
enum FPRegisterID {
s0 = 0, d0 = 0, v0 = 0,
s1 = 1, d1 = 1, v1 = 1,
s2 = 2, d2 = 2, v2 = 2,
s3 = 3, d3 = 3, v3 = 3,
s4 = 4, d4 = 4, v4 = 4,
s5 = 5, d5 = 5, v5 = 5,
s6 = 6, d6 = 6, v6 = 6,
s7 = 7, d7 = 7, v7 = 7,
s8 = 8, d8 = 8, v8 = 8,
s9 = 9, d9 = 9, v9 = 9,
s10 = 10, d10 = 10, v10 = 10,
s11 = 11, d11 = 11, v11 = 11,
s12 = 12, d12 = 12, v12 = 12,
s13 = 13, d13 = 13, v13 = 13,
s14 = 14, d14 = 14, v14 = 14,
s15 = 15, d15 = 15, v15 = 15,
s16 = 16, d16 = 16, v16 = 16,
s17 = 17, d17 = 17, v17 = 17,
s18 = 18, d18 = 18, v18 = 18,
s19 = 19, d19 = 19, v19 = 19,
s20 = 20, d20 = 20, v20 = 20,
s21 = 21, d21 = 21, v21 = 21,
s22 = 22, d22 = 22, v22 = 22,
s23 = 23, d23 = 23, v23 = 23,
s24 = 24, d24 = 24, v24 = 24,
s25 = 25, d25 = 25, v25 = 25,
s26 = 26, d26 = 26, v26 = 26,
s27 = 27, d27 = 27, v27 = 27,
s28 = 28, d28 = 28, v28 = 28,
s29 = 29, d29 = 29, v29 = 29,
s30 = 30, d30 = 30, v30 = 30,
s31 = 31, d31 = 31, v31 = 31, // Scratch register.
invalid_fpreg
};
typedef uint8_t Code;
typedef FPRegisterID Encoding;
typedef uint64_t SetType;
static const char* GetName(Code code) {
static const char* const Names[] =
{ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9",
"d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19",
"d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29",
"d30", "d31", "invalid" };
return Names[code];
}
static const char* GetName(uint32_t i) {
MOZ_ASSERT(i < TotalPhys);
return GetName(Code(i));
}
static Code FromName(const char* name);
static const Code Invalid = invalid_fpreg;
static const uint32_t Total = 64;
static const uint32_t TotalPhys = 32;
static const SetType AllMask = 0xFFFFFFFFFFFFFFFFULL;
static const SetType AllPhysMask = 0xFFFFFFFFULL;
static const SetType SpreadCoefficient = 0x100000001ULL;
static const uint32_t Allocatable = 31; // Without d31, the scratch register.
// d31 is the ScratchFloatReg.
static const SetType NonVolatileMask =
SetType((1 << FloatRegisters::d8) | (1 << FloatRegisters::d9) |
(1 << FloatRegisters::d10) | (1 << FloatRegisters::d11) |
(1 << FloatRegisters::d12) | (1 << FloatRegisters::d13) |
(1 << FloatRegisters::d14) | (1 << FloatRegisters::d15) |
(1 << FloatRegisters::d16) | (1 << FloatRegisters::d17) |
(1 << FloatRegisters::d18) | (1 << FloatRegisters::d19) |
(1 << FloatRegisters::d20) | (1 << FloatRegisters::d21) |
(1 << FloatRegisters::d22) | (1 << FloatRegisters::d23) |
(1 << FloatRegisters::d24) | (1 << FloatRegisters::d25) |
(1 << FloatRegisters::d26) | (1 << FloatRegisters::d27) |
(1 << FloatRegisters::d28) | (1 << FloatRegisters::d29) |
(1 << FloatRegisters::d30)) * SpreadCoefficient;
static const SetType VolatileMask = AllMask & ~NonVolatileMask;
static const SetType AllDoubleMask = AllMask;
static const SetType WrapperMask = VolatileMask;
// d31 is the ScratchFloatReg.
static const SetType NonAllocatableMask = (SetType(1) << FloatRegisters::d31) * SpreadCoefficient;
// Registers that can be allocated without being saved, generally.
static const SetType TempMask = VolatileMask & ~NonAllocatableMask;
static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
union RegisterContent {
float s;
double d;
};
enum Kind {
Double,
Single
};
};
// 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;
static const uint32_t ShadowStackSpace = 0;
static const uint32_t ABIStackAlignment = 16;
static const uint32_t CodeAlignment = 16;
static const bool StackKeptAligned = false;
// Although sp is only usable if 16-byte alignment is kept,
// the Pseudo-StackPointer enables use of 8-byte alignment.
static const uint32_t StackAlignment = 8;
static const uint32_t NativeFrameSize = 8;
struct FloatRegister
{
typedef FloatRegisters Codes;
typedef Codes::Code Code;
typedef Codes::Encoding Encoding;
typedef Codes::SetType SetType;
union RegisterContent {
float s;
double d;
};
constexpr FloatRegister(uint32_t code, FloatRegisters::Kind k)
: code_(FloatRegisters::Code(code & 31)),
k_(k)
{ }
explicit constexpr FloatRegister(uint32_t code)
: code_(FloatRegisters::Code(code & 31)),
k_(FloatRegisters::Kind(code >> 5))
{ }
constexpr FloatRegister()
: code_(FloatRegisters::Code(-1)),
k_(FloatRegisters::Double)
{ }
static uint32_t SetSize(SetType x) {
static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
x |= x >> FloatRegisters::TotalPhys;
x &= FloatRegisters::AllPhysMask;
return mozilla::CountPopulation32(x);
}
static FloatRegister FromCode(uint32_t i) {
MOZ_ASSERT(i < FloatRegisters::Total);
FloatRegister r(i);
return r;
}
Code code() const {
MOZ_ASSERT((uint32_t)code_ < FloatRegisters::Total);
return Code(code_ | (k_ << 5));
}
Encoding encoding() const {
return Encoding(code_);
}
const char* name() const {
return FloatRegisters::GetName(code());
}
bool volatile_() const {
return !!((SetType(1) << code()) & FloatRegisters::VolatileMask);
}
bool operator!=(FloatRegister other) const {
return other.code_ != code_ || other.k_ != k_;
}
bool operator==(FloatRegister other) const {
return other.code_ == code_ && other.k_ == k_;
}
bool aliases(FloatRegister other) const {
return other.code_ == code_;
}
uint32_t numAliased() const {
return 2;
}
static FloatRegisters::Kind otherkind(FloatRegisters::Kind k) {
if (k == FloatRegisters::Double)
return FloatRegisters::Single;
return FloatRegisters::Double;
}
void aliased(uint32_t aliasIdx, FloatRegister* ret) {
if (aliasIdx == 0)
*ret = *this;
else
*ret = FloatRegister(code_, otherkind(k_));
}
// This function mostly exists for the ARM backend. It is to ensure that two
// floating point registers' types are equivalent. e.g. S0 is not equivalent
// to D16, since S0 holds a float32, and D16 holds a Double.
// Since all floating point registers on x86 and x64 are equivalent, it is
// reasonable for this function to do the same.
bool equiv(FloatRegister other) const {
return k_ == other.k_;
}
MOZ_CONSTEXPR uint32_t size() const {
return k_ == FloatRegisters::Double ? sizeof(double) : sizeof(float);
}
uint32_t numAlignedAliased() {
return numAliased();
}
void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) {
MOZ_ASSERT(aliasIdx == 0);
aliased(aliasIdx, ret);
}
SetType alignedOrDominatedAliasedSet() const {
return Codes::SpreadCoefficient << code_;
}
bool isSingle() const {
return k_ == FloatRegisters::Single;
}
bool isDouble() const {
return k_ == FloatRegisters::Double;
}
bool isSimd128() const {
return false;
}
static uint32_t FirstBit(SetType x) {
JS_STATIC_ASSERT(sizeof(SetType) == 8);
return mozilla::CountTrailingZeroes64(x);
}
static uint32_t LastBit(SetType x) {
JS_STATIC_ASSERT(sizeof(SetType) == 8);
return 63 - mozilla::CountLeadingZeroes64(x);
}
static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister>& s);
static uint32_t GetSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
uint32_t getRegisterDumpOffsetInBytes();
public:
Code code_ : 8;
FloatRegisters::Kind k_ : 1;
};
// ARM/D32 has double registers that cannot be treated as float32.
// Luckily, ARMv8 doesn't have the same misfortune.
inline bool
hasUnaliasedDouble()
{
return false;
}
// ARM prior to ARMv8 also has doubles that alias multiple floats.
// Again, ARMv8 is in the clear.
inline bool
hasMultiAlias()
{
return false;
}
static const size_t AsmJSCheckedImmediateRange = 0;
static const size_t AsmJSImmediateRange = 0;
} // namespace jit
} // namespace js
#endif // jit_arm64_Architecture_arm64_h